views:

1123

answers:

13

Hello everyone, OK I need to design a way to keep track of how many of each item exists. There are approximately 26 items. I also need a way to find out if a certain combination of items exist.

For example, This is an engine for a card game. Each card has a different type and each card can have card attached to it. There need to be a certain combination of cards attached to a card for the player to do certain things in the game. To make this program streamlined, I would like to do something like

if (meetsCrit(2, water, 4, ground))
{
  do this()
}
else
{
  displayerror()
}

Thanks, -Michael

EDIT: SOLVED! I used a combination of techniques described in a few post below. Special mention to:

Jon Skeet, Rinat Abdullin, Frank,

Anyway here is what I did I made a class called pair which stores the type I'm looking for, and the number of that type. Then I used a Predicate Delegate to find all of that type and count how many there are, Then I compared it to number I was searching for and returned true or false respectively.

This is the code for it

public bool meetsCrit(params Pair[] specs)
        {
            foreach (Pair i in specs)
            {
                if (!(attached.FindAll(delegate(Card c) { return c.type == i.type; }).Count >= i.value))
                {
                    return false;
                }

            }
            return true;
        }
+12  A: 

use params.

Otávio Décio
params forces the user to use an array. His post was asking if he could do it in a way without using an array.
David Morton
No, params allows you to keep adding parameters to the call, it does NOT require you to use an array.
scwagner
scwagner: Please show a legal declaration of the method which doesn't use an array as the parameter type...
Jon Skeet
The method would need to be a Method(params T[] arr) declaration, but the caller of the method would not need to create the array. The caller could do Method(t1, t2)
Timothy Carter
The caller wouldn't be explicitly creating the array, but I think it's fair to say that the call is still *using* an array.
Jon Skeet
@Jon Skeet: Yes, but the user doesn't know that, which is contrary to what David said.
R. Bemrose
Where did David say the user would *know* they're using an array? "params" involves using an array. The question asks for a solution which *doesn't* use an array. Seems pretty straightforward to me.
Jon Skeet
+1  A: 

Other than using the params keyword, which will force you to add your items in an array, there's currently no support for this in C#. C# 4.0 will introduce optional parameters into the language, but I'm not sure if that will be arbitrary enough for you.

David Morton
No, params allows you to keep adding parameters to the call, it does NOT require you to use an array.
scwagner
May I ask you... what does the data type look like within the method? Isn't it an array?
David Morton
+1 to David's comments. If you try to declare a params parameter without it being an array type, you get: error CS0225: The params parameter must be a single dimensional array
Jon Skeet
http://msdn.microsoft.com/en-us/library/aa645765(VS.71).aspx"Parameter arrays", courtesy of the C# language specification.
David Morton
It depends how you slice the OP questions. He asks how do you *pass* an arbitrary number of variables to a method, without using an array. So the caller doesn't have to use an array with the params keyword. So scwagner is right. The implementation will have to use an array, so David is correct too.
Alan
The caller uses an array - just implicitly. Furthermore, I'd point out that the question just asks for "a good way to do this other than an array". Where did the "the user doesn't see the array" part come from?
Jon Skeet
A: 

I would take a Predicate which can be evaluated against your data source and then evaluate that, returning the result from the Predicate.

casperOne
A: 

How are these items stored? They should be stored in a item collection object. It should have the methods necessary to inspect it's innards.

Or use an params array of objects implementing your IItem interface.

Øyvind Skaar
+3  A: 

The params keyword allows you to pass an arbitrary number of parameters in non-array form. They will, however, be converted to an array inside the function. I don't know of any programming construct that's going to be different from that in any language.

Jekke
+10  A: 

A params array is the obvious answer. An alternative is to create a class to represent the criteria. You could then use collection initializers, something like:

bool result = myCollection.Contains(new Criteria {
                 { 2, Item.Balloon },
                 { 4, Item.Cupcake }
              });

But if that's not useful either, we definitely need more information in the question.

If you could provide an example the syntax that you'd like to call the method with, that would certainly help us.

Jon Skeet
+12  A: 

Use the params keyword to pass a variable number of arguments:

   private static void HowMayItems<T>(params T[] values) {
        foreach (T val in values) { 
            //Query how many items here..
        }
    }

Also you can create a Predicate and pass it a params filter. In the method you could return the union of all results. Somthing like this:

public class Item { 
    public string Name { get; set; }
}
public class ItemFilter {
    public string Name { get; set; }
    public ItemFilter(string name) {
        Name = name;
    }
    public bool FilterByName(Item i) {
        return i.Name.Equals(Name);
    }
}

public class ItemsTest {
    private static List<Item> HowMayItems(List<Item> l,params ItemFilter[] values)
    {
        List<Item> results= new List<Item>();
        foreach(ItemFilter f in values){
            Predicate<Item> p = new Predicate<Item>(f.FilterByName);
            List<Item> subList = l.FindAll(p);
            results.Concat(subList);
        }
        return results;
    }
}

EDIT:

OK, how about my mixed nuts version :):

public enum ItemTypes{
    Balloon,
    Cupcake,
    WaterMelon
    //Add the rest of the 26 items here...
}

public class ItemFilter {
    private ItemTypes Type { get; set; }
    public ItemFilter(ItemTypes type) {
        Type = type;
    }
    public bool FilterByType(ItemTypes type) {
        return this.Type == type;
    }
}

public class PicnicTable {
    private List<ItemTypes> Items;

    public PicnicTable() {
        Items = new List<ItemTypes>();
    }

    public void AddItem(ItemTypes item) {
        Items.Add(item);
    }

    public int HowMayItems(ItemTypes item)
    {
        ItemFilter filter = new ItemFilter(item);
        Predicate<ItemTypes> p = new Predicate<ItemTypes>(filter.FilterByType);
        List<ItemTypes> result = Items.FindAll(p);
        return result.Count;
    }
}

public class ItemsTest {
    public static void main(string[] args) {
        PicnicTable table = new PicnicTable();
        table.AddItem(ItemTypes.Balloon);
        table.AddItem(ItemTypes.Cupcake);
        table.AddItem(ItemTypes.Balloon);
        table.AddItem(ItemTypes.WaterMelon);
        Console.Out.WriteLine("How Many Cupcakes?: {0}", table.HowMayItems(ItemTypes.Cupcake));
    }
}
Igor Zelaya
+1 Nice answer - lots of information and alternatives
Mark Brittingham
A: 
bool doTheseItemsExist(List criteria) { .. }

Can you not simply pass in a List of objects? Technically, it's similar to passing an array, but it's easy to work with, and you don't have to load up the call stack with what could be a potentially large parameter list if you use params. (That last part, of course, depends entirely on how many criteria you intend to search on.)

(EDIT: Actually, that List would need to be a mapping of object type and expected number.)

But the gentlemen who suggested that your collection should expose these queries in your collection is probably the winner here. One possibility:

bool MyCollection.HasBalloons(int numBalloons);
bool MyCollection.HasCupcakes(int numCupcakes);
bool MyCollection.HasPartyHats(int numHats);

But then the bool result is ambiguous to the caller... has exactly this many? has at least this many? So perhaps a better direction is:

int MyCollection.NumBalloons();
int MyCollection.NumCupcakes();
int MyCollection.NumPartyHats();

You then don't need some extra function with an arbitrary set of parameters. You can simply inspect the collection as needed.

JMD
A: 

An array would probably do the job, but it is not exactly elegant. Do you have a class with some collections with the different type of items in? Another solution that's a little more precise is using a Dictionary

Dictionary<String, int> numberOfEach = new Dictionary<string, int>();
numberOfEach.Add("balloons", 2);
numberOfEach.Add("cupcakes", 4);

Then you'll have a method in your class (the one with the collections)

public bool correctAmmounts(Dictionary<String, int> numberOfEach)
{
    return collection1.Count == numberOfEach["balloons"] &&
           collection2.Count == numberOfEach["cupcakes"];
}

Where collection1 has some balloons and collection2 has some cupcakes.

You'll have to edit your method if the number of different types change, though.

Yngve Sneen Lindal
A: 

I wonder if the question is somewhat broken. If you are asking some object if it contains n of this and y of that, then your object can be enumerated. In that case it should implement IEnumerable where T would be the "common denominator of your contents". from here on you can use e.g. LINQ to get answers to your questions or encapsulate such questions int the objects and provide e.g. a Criteria system as Jon has shown...

flq
A: 

This sounds like a good opportunity to do some OOP:

say you have some classes for representing your items

public class Item
{
  public string Name{ get; set; }
  // Here put all the properties that are common to all items.
}

public class Cupcake : Item
{
  // By inheriting from Item, it will have Name 
  //  along with anything else you define there

  public bool HasFrosting{ get; set; }
  // Put whatever else Cupcake needs here
}

public class Baloon : Item
{
  public Color MyColor{ get; set; }
}

Then in your application code

// From your post, I can't tell where you're getting
//  your items, so I'll just put a placeholder here for now.
//  I assume this returns all kinds of items:
//  Cupcakes, Baloons, and other fun stuff
Item[] items = GetItemsFromSomewhere();

int cupcakeCount = GetCupcakeCount(items);

...

public int GetCupcakeCount( Item[] items )
{
  int cupcakeCount = 0;

  // This is the code for checking types of items
  //  you could use a LINQ statement or whatever you like
  //  but i just put a foreach loop here for simplicity
  foreach( Item item in items )
  {
    // Check the types of each object, 
    if(item is Cupcake)
     ++cupcakeCount;
  }

  return cupcakeCount;
}

Even if you can do this another way, consider OOP since it seems that you need to distinguish between types of objects and compare them. This might be a good way to accurately represent your system.

TJB
+1  A: 

OK, because I felt like it, some code to figure out how many instances you have of an arbitrary number of types in some list...

I need the funny counter:

  public class FunnyCounter
  {
    Dictionary<Type, int> restrictions = new Dictionary<Type, int>();
    public FunnyCounter Add<T>(int appears)
    {
      restrictions.Add(typeof(T), appears);
      return this;
    }

    public void PassThrough(object o)
    {
      if (restrictions.ContainsKey(o.GetType()))
        restrictions[o.GetType()] = restrictions[o.GetType()] - 1;
    }

    public bool SatisfiedAll()
    {
      return restrictions.Values.Aggregate(true, (b, i) => b && i == 0);
    }

Now with a list like

List<Stuff> l = new List<Stuff> { new Ball(), new Ball(), new Bucket() };

I do...

FunnyCounter counter = new FunnyCounter();
counter.Add<Ball>(2).Add<Bucket>(1);
l.ForEach(counter.PassThrough);
Console.WriteLine(counter.SatisfiedAll());
flq
+1  A: 

Here's how I would have done this in a simplified manner:

var collection = new[] {Item.Baloon, Item.Cupcake, Item.Baloon, Item.Coke};

var result = collection.Contains(2.Baloons(), 1.Cupcakes());

where:

public enum Item
{
 Baloon,
 Cupcake,
 Coke
}

public static class TableExtensions
{
 public static Pair<Item, int> Baloons(this int count)
 {
  return Tuple.From(Item.Baloon, count);
 }

 public static Pair<Item, int> Cupcakes(this int count)
 {
  return Tuple.From(Item.Cupcake, count);
 }

 public static bool Contains(this IEnumerable<Item> self, 
  params Pair<Item, int>[] criteria)
 {
  foreach (var pair in criteria)
  {
   var search = pair.Key;
   if (self.Count(item => item == search) < pair.Value)
    return false;
  }
  return true;
 }
}

Remarks:

  • You don't care about the order of items in the parameters.
  • I'm using enums to represent objects, but these could be objects as well
  • Tuples come from the Lokad.Shared
  • Contains currently searches for "at least X items". We could have more flexibility by passing around criteria objects that are actually predicates for the enumerable - rules. This would keep syntax the same, but allow to have more options (i.e.: AtLeast, AtMost, X.VegetarianSets, Y.Drinks, etc). If interested, check out the predefined validation rules from the validation and business rules application block for the similar approach to rule composition.
Rinat Abdullin