如何检查字典中的所有值在 C# 中是否相同?
How to Check All Values in Dictionary is same in C#?
我有一个字典,我想写一个方法来检查这个字典中的所有值是否相同。
词典类型:
Dictionary<string, List<string>>
列表 {1,2,3}` 和 {2,1,3} 在我的例子中是相同的。
我以前为简单的数据类型值做过这个,但我找不到新要求的逻辑,请帮助我。
对于简单值:
MyDict.GroupBy(x => x.Value).Where(x => x.Count() > 1)
我也写了一个通用方法来以这种方式比较两种数据类型。
// 1
// Require that the counts are equal
if (a.Count != b.Count)
{
return false;
}
// 2
// Initialize new Dictionary of the type
Dictionary<T, int> d = new Dictionary<T, int>();
// 3
// Add each key's frequency from collection A to the Dictionary
foreach (T item in a)
{
int c;
if (d.TryGetValue(item, out c))
{
d[item] = c + 1;
}
else
{
d.Add(item, 1);
}
}
// 4
// Add each key's frequency from collection B to the Dictionary
// Return early if we detect a mismatch
foreach (T item in b)
{
int c;
if (d.TryGetValue(item, out c))
{
if (c == 0)
{
return false;
}
else
{
d[item] = c - 1;
}
}
else
{
// Not in dictionary
return false;
}
}
// 5
// Verify that all frequencies are zero
foreach (int v in d.Values)
{
if (v != 0)
{
return false;
}
}
// 6
// We know the collections are equal
return true;
为 List<string>
实施 IEqualityComparer
,根据内容比较两个列表。然后只需在 Values
上使用 Distinct
并检查计数:
dictionary.Values.Distinct(new ListEqualityComparer()).Count() == 1
Selman22 的答案非常有效 - 您也可以为您的 Dictionary<string, List<string>>
执行此操作,而无需自己实施 IEqualityComparer
:
var firstValue = dictionary.Values.First().OrderBy(x => x);
return dictionary.Values.All (x => x.OrderBy(y => y).SequenceEqual(firstValue));
我们将第一个值与其他所有值进行比较,并在每种情况下检查是否相等。请注意,List<string>.OrderBy(x => x)
只是按字母顺序对字符串列表进行排序。
这应该可以解决问题
var lists = dic.Select(kv => kv.Value.OrderBy(x => x)).ToList();
var first = lists.First();
var areEqual = lists.Skip(1).All(hs => hs.SequenceEqual(first));
您需要添加一些检查才能使其适用于空箱。
...或者如果您想采用@Selman 的方法,这里是 IEqualityComparer
:
的实现
class SequenceComparer<T>:IEqualityComparer<IEnumerable<T>>
{
public bool Equals(IEnumerable<T> left, IEnumerable<T> right)
{
return left.OrderBy(x => x).SequenceEqual(right.OrderBy(x => x));
}
public int GetHashCode(IEnumerable<T> item)
{
//no need to sort because XOR is commutative
return item.Aggregate(0, (acc, val) => val.GetHashCode() ^ acc);
}
}
您可以使用 HashSet<T>
将这两种方法的优点结合起来做一个变体,如果您有很多候选人要测试,这可能会更有效:
HashSet<IEnumerable<int>> hs = new HashSet<IEnumerable<int>>(new SequenceComparer<int>());
hs.Add(dic.First().Value);
var allEqual = dic.All(kvp => !hs.Add(kvp.Value));
这使用了 HashSets 的特性,该特性不允许添加多个被认为与集合中已有的项目相同的项目。我们使 HashSet 使用上面的自定义 IEqualityComparer
...
所以我们在开始之前从字典中插入一个任意项,然后在允许另一个项进入集合的那一刻(即 hs.Add(kvp.Value)
是 true
),我们可以说有超过集合中的一项并尽早退出。 .All
自动执行此操作。
它不是最快的解决方案,但对我有用:
bool AreEqual = l1.Intersect(l2).ToList().Count() == l1.Count() && l1.Count() == l2.Count();
我有一个字典,我想写一个方法来检查这个字典中的所有值是否相同。 词典类型:
Dictionary<string, List<string>>
列表 {1,2,3}` 和 {2,1,3} 在我的例子中是相同的。
我以前为简单的数据类型值做过这个,但我找不到新要求的逻辑,请帮助我。 对于简单值: MyDict.GroupBy(x => x.Value).Where(x => x.Count() > 1)
我也写了一个通用方法来以这种方式比较两种数据类型。
// 1
// Require that the counts are equal
if (a.Count != b.Count)
{
return false;
}
// 2
// Initialize new Dictionary of the type
Dictionary<T, int> d = new Dictionary<T, int>();
// 3
// Add each key's frequency from collection A to the Dictionary
foreach (T item in a)
{
int c;
if (d.TryGetValue(item, out c))
{
d[item] = c + 1;
}
else
{
d.Add(item, 1);
}
}
// 4
// Add each key's frequency from collection B to the Dictionary
// Return early if we detect a mismatch
foreach (T item in b)
{
int c;
if (d.TryGetValue(item, out c))
{
if (c == 0)
{
return false;
}
else
{
d[item] = c - 1;
}
}
else
{
// Not in dictionary
return false;
}
}
// 5
// Verify that all frequencies are zero
foreach (int v in d.Values)
{
if (v != 0)
{
return false;
}
}
// 6
// We know the collections are equal
return true;
为 List<string>
实施 IEqualityComparer
,根据内容比较两个列表。然后只需在 Values
上使用 Distinct
并检查计数:
dictionary.Values.Distinct(new ListEqualityComparer()).Count() == 1
Selman22 的答案非常有效 - 您也可以为您的 Dictionary<string, List<string>>
执行此操作,而无需自己实施 IEqualityComparer
:
var firstValue = dictionary.Values.First().OrderBy(x => x);
return dictionary.Values.All (x => x.OrderBy(y => y).SequenceEqual(firstValue));
我们将第一个值与其他所有值进行比较,并在每种情况下检查是否相等。请注意,List<string>.OrderBy(x => x)
只是按字母顺序对字符串列表进行排序。
这应该可以解决问题
var lists = dic.Select(kv => kv.Value.OrderBy(x => x)).ToList();
var first = lists.First();
var areEqual = lists.Skip(1).All(hs => hs.SequenceEqual(first));
您需要添加一些检查才能使其适用于空箱。
...或者如果您想采用@Selman 的方法,这里是 IEqualityComparer
:
class SequenceComparer<T>:IEqualityComparer<IEnumerable<T>>
{
public bool Equals(IEnumerable<T> left, IEnumerable<T> right)
{
return left.OrderBy(x => x).SequenceEqual(right.OrderBy(x => x));
}
public int GetHashCode(IEnumerable<T> item)
{
//no need to sort because XOR is commutative
return item.Aggregate(0, (acc, val) => val.GetHashCode() ^ acc);
}
}
您可以使用 HashSet<T>
将这两种方法的优点结合起来做一个变体,如果您有很多候选人要测试,这可能会更有效:
HashSet<IEnumerable<int>> hs = new HashSet<IEnumerable<int>>(new SequenceComparer<int>());
hs.Add(dic.First().Value);
var allEqual = dic.All(kvp => !hs.Add(kvp.Value));
这使用了 HashSets 的特性,该特性不允许添加多个被认为与集合中已有的项目相同的项目。我们使 HashSet 使用上面的自定义 IEqualityComparer
...
所以我们在开始之前从字典中插入一个任意项,然后在允许另一个项进入集合的那一刻(即 hs.Add(kvp.Value)
是 true
),我们可以说有超过集合中的一项并尽早退出。 .All
自动执行此操作。
它不是最快的解决方案,但对我有用:
bool AreEqual = l1.Intersect(l2).ToList().Count() == l1.Count() && l1.Count() == l2.Count();