0

I'm debating back and forth about this. I want to have a Hashtable (or dictionary... both seem to do just as good for this purpose)

I have a class Item which has ItemNumber, Description, and Quantity. I have a class ItemCollection (which currently uses a Item array) that I want to use either a hashtable or dictionary. In my Item class I use the ItemNumber to compare if item a == item b.

So should I move ItemNumber up to ItemCollection as the Key for my hashtable or should I just leave it. Part of me wants to leave it since I generate a table based on Item.. But another part of me thinks that is redundant and silly.

What my end goal is to see if a ItemCollection which is the items shipped equals another ItemCollection (the order) by doing something like this.

foreach(var package in shipments)
    foreach(var item in package)
       shipOrder += item;

return mainOrder==shipOrder;

I hope this makes sense. My head is still a little foggy from being sick, so I f i need to clarify some more please let me know.

EDIT

ok so I thought about what everyone said, and tried it, and most of tests pass except for one, and i can't figure out why. So i'll post what I have and see if you guys can figure out my stupid mistake.

public class Item
{
    public Item(object[] array)
    {
        ItemNumber = array[0].ToString();
        Description = array[1].ToString();
        Quantity = (int)array[2];
    }
    public Item(string item, string desc, int qty)
    {
        ItemNumber = item;
        Description = desc;
        Quantity = qty;
    }
    /// <summary>
    /// </summary>
    public string ItemNumber;
    public string Description;
    public int Quantity;

    public override bool Equals(object obj)
    {
        //return compareTwoItems(this, (Item)obj);
        return this.GetHashCode() == obj.GetHashCode();
    }
    public static bool operator ==(Item ic1, Item ic2)
    {
        return compareTwoItems(ic1, ic2);
    }
    public static bool operator !=(Item ic1, Item ic2)
    {
        return !compareTwoItems(ic1, ic2);
    }
    private static bool compareTwoItems(Item ic1, Item ic2)
    {
        return (ic1.ItemNumber == ic2.ItemNumber)
            && (ic1.Quantity == ic2.Quantity);
    }
    public override int GetHashCode()
    {
        return ItemNumber.GetHashCode() ^ Quantity;
    }
}
public class ItemCollection : System.Collections.Generic.SortedDictionary<string, Item>
{
    public ItemCollection()
    {
    }
    public void AddItem(Item i)
    {
        this.Add(i.ItemNumber, i);
    }

    public string TrackNumber = "";
    /// <summary>
    /// Check to see if the Two ItemCollections have the same Quantity of items. If not it may be that the order was not completed
    /// </summary>
    /// <param name="obj">the sales order items</param>
    /// <returns>True if the quantity of the two collections are the same.</returns>
    /// <exception cref="ArgumentException">If the collections have different counts, or if ItemNumbers differ in one of the elements</exception>
    public override bool Equals(object obj)
    {
        return this.GetHashCode() == ((ItemCollection)obj).GetHashCode();
    }
    public override int GetHashCode()
    {
        int hash = 0;
        foreach (var item in this.Values)
            hash ^= item.GetHashCode();

        return hash;
    }
    /// <summary>
    /// Check to see if the Two ItemCollections have the same Quantity of items. If not it may be that the order was not completed
    /// </summary>
    /// <param name="ic1">the sales order items</param>
    /// <param name="ic2">the shipping ticket items</param>
    /// <returns>True if the quantity of the two collections are the same.</returns>
    /// <exception cref="ArgumentException">If the collections have different counts, or if ItemNumbers differ in one of the elements</exception>
    public static bool operator ==(ItemCollection ic1, ItemCollection ic2)
    {
        return ic1.Equals(ic2);
    }
    public static bool operator !=(ItemCollection ic1, ItemCollection ic2)
    {
        return !ic1.Equals(ic2);
    }
}

Unit Tests

    [TestMethod, TestCategory("ItemColl")]
    public void ItemCollectionPassTest()
    {
        MSSqlDatabase db = new MSSqlDatabase();
        ItemCollection salesOrder = db.GetItemsCollectionFromSalesOrder(4231);
        ItemCollection items = db.GetItemsCollectionFromSalesOrder(4231);

        Assert.AreEqual(salesOrder, items); //passes
        Assert.IsTrue(salesOrder == items); //passes
    }
    [TestMethod, TestCategory("ItemColl")]
    public void ItemCollectionDifferentQuantity()
    {
        MSSqlDatabase db = new MSSqlDatabase();
        ItemCollection salesOrder1 = db.GetItemsCollectionFromSalesOrder(4231);
        ItemCollection salesOrder2 = db.GetItemsCollectionFromSalesOrder(4232);
        Assert.AreNotEqual(salesOrder1, salesOrder2); //passes
        Assert.IsTrue(salesOrder1 != salesOrder2); //passes

        ItemCollection[] items = db.GetItemsCollectionFromShipping(4231);
        ItemCollection test = items[0];
        Assert.AreNotEqual(salesOrder1, test); //Fails
        CollectionAssert.AreNotEqual(salesOrder1, items[0]); // passes
    }

.... and now for some reason it works... I changed one thing in my code in teh GetHash Method in Item (forgot to xor the quantity with the hash from the ItemNumber) and now they pass... weird. but i'll post my code anyway as it might help someone.

Robert Snyder
  • 2,399
  • 4
  • 33
  • 65

2 Answers2

1

I agree with scarle88, I think your ItemCollection should extend Dictionary or Hashtable. The dictionary is going to need a unique key for each item, and you could use the item number for that (if your requirements are that each item is unique).

Like this:

public class Item {
    public string ItemNumber { get; set; }
    public string Description { get; set; }
    public string Quantity { get; set; }

    public override bool Equals(Object o) {
        // Implement your Equals here
    }
    public override int GetHashCode() {
        // Implement your hash code method here
    }
}

public class ItemCollection : Dictionary<string, Item> {
    public override bool Equals(Object o) {
        // Implement your collection's Equals method here
    }
}

I'm not sure what your requirements are for comparing the collections. But if you only care to see that both collections have the same items, you could implement ItemCollection.Equals like this:

public override bool Equals(Object o) {
    if (o == null)
        return false;
    if (!(o is ItemCollection))
        return false;
    var other = o as ItemCollection;

    // check the 'this' collection
    foreach (var item in this.Keys) {
        if (item != null && !other.ContainsKey(item.ItemNumber))
            return false;

    // check the 'other' collection
    foreach (var item in other.Keys) {
        if (item != null && !this.ContainsKey(item.ItemNumber))
            return false;
    return true;
}
Daniel Gabriel
  • 3,939
  • 2
  • 26
  • 37
  • thank you very much for that example. Let me show you what I have, because for a weird reason my unit test is failing – Robert Snyder Dec 06 '12 at 21:12
  • I'm glad it started working. I can't tell you why it failed before, it looks like that would depend on the data you are getting from the database. – Daniel Gabriel Dec 06 '12 at 21:33
  • yeah it was saying something like Expected was ItemCollection and Found ItemCollection.. I was starting to go crazy because it wasn't working. Now it is. – Robert Snyder Dec 06 '12 at 21:48
0

You could use a HashTable<T> as your collection and in your Item class override the base object Equals() and the GetHashCode() methods.

Why is it important to override GetHashCode when Equals method is overridden?

Community
  • 1
  • 1
Mark Vickery
  • 1,927
  • 3
  • 22
  • 34
  • i already did that, but on a note about that my item number is not a number it is a string. (I don't like it but it is out of my control) should I make the string into a byte array and xor the byte array? – Robert Snyder Dec 06 '12 at 19:11
  • 1
    In your Item class GetHashCode override you can simply return the hashcode of the ItemNumber eg: return _itemNumber.GetHashCode(); – Mark Vickery Dec 06 '12 at 19:15
  • Not to be a jerk, but your answer doesn't show if i should keep ItemNumber in my Item class or not. The other parts make sense and happened to answer another of my lingering questions – Robert Snyder Dec 06 '12 at 19:31
  • 1
    I would keep ItemNumber in the Item class as it is part of Item. Another idea is your item collection could extend HashTable (say) so that it behaves just like a HashTable but you could override the == operator to tell if two item collections are the same. (Think thats about as much as I can offer given the info in your question) – Mark Vickery Dec 06 '12 at 20:10