在列表中检查唯一项(C#)

3

我有一个项目列表。每个项目是一个包含两个字段的对象。第一个字段是类型为Dictionary<string, List<string>的字典,第二个字段是int类型。
现在我想检查是否存在两个字段都是唯一的元素。
对于字典字段,我只关心键部分,而不是值。因此,在项之间,值部分可能相同。
这个列表中必须是键必须唯一的。

如果存在这样的元素,我希望能够获取该元素在列表中的位置。

希望清楚明了。

为了澄清:

以下是我的类

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;
        }
    }
}

在我的代码的另一部分,我有一个名为recordItems的List<RecordItem>类型列表。

我想检查recordItems列表中是否有RecordItem对象,该对象基于其字段在列表中是唯一的。字典中的键必须是唯一的,MachineNr也必须是唯一的。

提前感谢。


3
你的意思是“两个字段在列表中都是唯一的”吗?如果你提供一些实际的代码,会更有帮助…… - Jon Skeet
你能更清楚地描述一下对于字典字段,“unique”是什么意思吗?如果两个字典具有完全相同的键集,它们是否“相同”? - Eric Lippert
当你说“unique”时,你需要解释你所指的是什么。无论如何,一个字典对必须有一个唯一的键。因此,你需要更具体地定义你想要定义为唯一记录集的内容。 - James
通过说“独特”,我指的是在持有这些值的对象之间是独一无二的。所以我可以有两个持有相同键的字典对象的对象。我正在寻找在这个意义上是独一无二的对象。 - kamilwydrzycki
3个回答

3

我相信以下的LINQ解决方案可以解决问题。

假设:

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; } 
}

然后:

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() )
              ;

为了完整起见,这是我的测试数据:

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} } } );

2

尝试重写GetHashCode方法并向您的项实现IEquatable

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;
        }
    }
}

GetHashCode会对字典中的键和MachineNr的所有散列值求和,以确保两个字典具有相同的键。
然后,您可以在列表中使用Contains方法。
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));

输出结果应为true。


抱歉,之前忘了提到在上面的代码中你可以使用lstItem.IndexOf(i1)或lstItem.IndexOf(i2)来获取i1的位置,因为i1等于i2。 - Anton Setiawan

1

你可以创建一个新的查找表来存储你的键和索引位置。遍历你的集合,将你的键添加到查找表中,并增加出现次数并追加索引位置。

然后检查你的查找表,查找索引位置集合长度等于1的条目。

这里是我所说的填充查找表的意思:

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; }
}

编辑:现在,我已经看到了你的代码,我觉得它会变得非常混乱。我错误地假设你的记录项有一个属性:

KeyValuePair<string, List<string>>

而不是:

Dictionary<string, List<string>>

我不再明白您需要检索哪些索引位置。恐怕您需要进一步澄清。具体而言,您说:

字典中的键必须是唯一的

但是该字典有很多键。您是指键集合必须是唯一的吗?


我在考虑类似的事情,但是还有另一个字段必须是唯一的(该字段的类型为int)。 - kamilwydrzycki
不是的,由于问题的领域,我使用每个RecordItem对象一个密钥。并且它在多个对象中相同。我正在寻找在对象集合中具有唯一键的对象。希望这样稍微清晰一些。 - kamilwydrzycki
在这种情况下,我认为整个数据结构可能或应该进行重构。可能是将其重构为一个新的集合对象,其中包含您的唯一性标志作为属性。 - grenade

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接