tags:

views:

304

answers:

3

Hello

I have a list of items. Item is a object with two fields. One field is of type Dictionary<string, List<string> and second filed is of type int. Now I want to check if there is an item with both fields unique in that list. With dict field I care about key part, not value so value part may be the same between items. It is key that must be unique in that list.

If such element exists I want to be able to get position of that element in my list.

Hope it is clear.

To clarify -

Here is my class

namespace Polstyr
{
class RecordItem
{
    Dictionary<string, List<string>> dict;

    public string MachineNr { get; set; }

    public RecordItem()
    {
        dict = new Dictionary<string, List<string>>();
    }

    public void AddToDict(string value, List<string> list)
    {
        dict.Add(value, list);
    }

    public Dictionary<string, List<string>> GetDictionary
    {
        get
        {
            return dict;
        }
    }
}

}

In other part of my code I have list of type List<RecordItem> named recordItems.

I would like to check there is RecordItem object in recordItems list that is unique in that list based on it's fields. Key in dict must be unique and MachineNr must be unique.

Thanks in advance.

+2  A: 

Try to override GetHashCode method and implement IEquatable to your item

public class RecordItem:IEquatable<RecordItem>
{
...

    public override int GetHashCode()
    {
        int i=0;
        if (dict != null)
        {
            foreach (KeyValuePair<string, List<string>> pair in dict)
                i += pair.Key.GetHashCode();
        }
        i += MachineNr.GetHashCode();
        return i;
    }

    public bool Equals(RecordItem item)
    {

        if (MachineNr != item.MachineNr)
            return false;
        else
        {
            if ((dict != null && item.dict == null) || (dict == null && item.dict != null))
                return false;
            else if (dict != null && item.dict != null)
            {
                foreach (KeyValuePair<string, List<string>> pair in dict)
                {
                    if (!item.dict.ContainsKey(pair.Key))
                        return false;
                }
                return true;
            }
            else return true;
        }
    }
}

The GetHashCode will sum all of the hash value of key in your dictionary and MachineNr. That will ensure 2 dictionaries will have the same keys.
Then, you can use Contains method in your List.

RecordItem i1 = new RecordItem{ MachineNr="M1"};
i1.AddToDict("1", new List<string>{ "A","B"});
i1.AddToDict("2", null);

RecordItem i2 = new RecordItem{MachineNr = "M1"};
i2.AddToDict("1", null);
i2.AddToDict("2", new List<string> { "A", "B" });

List<RecordItem> lstItem = new List<RecordItem>();
lstItem.Add(i1);
Console.WriteLine(lstItem.Contains(i2));

The output should be true

Anton Setiawan
Could You provide some sample code?
kamilo
Sorry, forgot to mention that you can use lstItem.IndexOf(i1) or lstItem.IndexOf(i2) in above code to get the position of i1 since i1 equals to i2
Anton Setiawan
+1  A: 

You could create a new lookup table to store your keys and index positions. Iterate your collection adding your keys to the lookup table and incrementing the occurence count and appending the index position.

Then check your lookup table for entries with index position collection lengths equal to 1.

Here's what I mean by populating a lookup table:

class Main
{
    void DoIt()
    {
        Thing[] collection = new [] { new Thing(), new Thing() };
        var lookupTable = new Dictionary<KeyValuePair<int, string>, List<int>>();
        int index = 0;
        foreach (Thing item in collection)
        {
            KeyValuePair<int, string> key = new KeyValuePair<int, string>(item.Bar, item.Foo.Key);
            if (!lookupTable.ContainsKey(key))
                lookupTable.Add(key, new List<int>());
            lookupTable[key].Add(index++);
        }
    }
}

class Thing
{
    public KeyValuePair<string, List<string>> Foo { get; set; }
    public int Bar { get; set; }
}

EDIT: Now, I've seen your code, I reckon it's going to get very confusing. I wrongly assumed your record item had a property of:

KeyValuePair<string, List<string>>

rather than:

Dictionary<string, List<string>>

I no longer understand which index position(s) you need to retrieve. You'll have to clarify further I'm afraid. Specifically, you say:

Key in dict must be unique

but the dictionary has lots of keys. Do you mean the key collection must be unique?

grenade
I was thinking about something similar but there is other field that must be unique (field if type int)
kamilo
No, because of the domain of the problem I use one key per RecordItem object. And it be the same in several objects. I'm looking for object with unique key in object collection. Hope it is a little bit clearer.
kamilo
In that case, I think the whole data structure could or should be refactored. Possibly, into a new collection object that has your uniqueness flags baked in as properties.
grenade
+2  A: 

Here's a LINQ solution which I believe will work.

Given:

class A
{
    // your int field
    public int Bar{get; set;} 

    // your dictionary (the value is irrelevant so I've just used bool)
    public Dictionary<string, bool> Foo{ get; set; } 
}

Then:

var nonUniqueFoo = list.SelectMany(a => a.Foo.Keys)
                    .GroupBy( s => s)
                    .Where( g => g.Count() > 1)
                    .Select(g => g.Key);


var nonUniqueBar = list.Select(a => a.Bar)
                    .GroupBy( i => i)
                    .Where( g => g.Count() > 1)
                    .Select(g => g.Key);

var uniqueObjects = list.Where( a=> !nonUniqueBar.Contains(a.Bar) )
                       .Where( a => !nonUniqueFoo.Intersect(a.Foo.Keys).Any() )
     ;

For completeness, here's my test data:

List<A> list = new List<A>();
list.Add( new A{ Bar=1, Foo = new Dictionary<string, bool>{ {"123", true} } } );
list.Add( new A{ Bar=2, Foo = new Dictionary<string, bool>{ {"456", true}, {"789", true}, {"567", true} } } );
list.Add( new A{ Bar=3, Foo = new Dictionary<string, bool>{ {"AAA", true}, {"456", true}, {"567", true} } } );
Winston Smith