【问题标题】:how to find members that exist in at least two lists in a list of lists如何在列表列表中查找至少存在于两个列表中的成员
【发布时间】:2015-07-13 12:58:19
【问题描述】:

我有一个列表数组:

var stringLists = new List<string>[] 
{ 
    new List<string>(){ "a", "b", "c" },
    new List<string>(){ "d", "b", "c" },
    new List<string>(){ "a", "d", "c" }
};

我想提取至少 2 个列表中常见的所有元素。所以对于这个例子,我应该得到所有元素["a", "b", "c", "d"]。我知道如何找到所有人共有的元素,但想不出任何方法来解决这个问题。

【问题讨论】:

  • 你可以启动here并修改它以使用第三个集合。
  • 您可以使用 SelectMany 从所有列表中创建一个列表,然后选择至少出现 2 次的所有元素。
  • 列表中是否可以重复?我的意思是 list new List(){ "a", "a", "a" } 可能会出现吗?

标签: c# list


【解决方案1】:

你可以这样使用:

var result = stringLists.SelectMany(l => l.Distinct())
                        .GroupBy(e => e)
                        .Where(g => g.Count() >= 2)
                        .Select(g => g.Key);

只是为了好玩一些迭代解决方案:

var seen = new HashSet<string>();
var current = new HashSet<string>();
var result = new HashSet<string>();
foreach (var list in stringLists)
{
    foreach(var element in list)
        if(current.Add(element) && !seen.Add(element))
            result.Add(element);

    current.Clear();
}

或:

var already_seen = new Dictionary<string, bool>();
foreach(var list in stringLists)
    foreach(var element in list.Distinct())
         already_seen[element] = already_seen.ContainsKey(element);

var result = already_seen.Where(kvp => kvp.Value).Select(kvp => kvp.Key);

或(受Tim's answer启发):

int tmp;
var items = new Dictionary<string,int>();

foreach(var str in stringLists.SelectMany(l => l.Distinct()))
{
    items.TryGetValue(str, out tmp);
    items[str] = tmp + 1;
}

var result = items.Where(kv => kv.Value >= 2).Select(kv => kv.Key);

【讨论】:

  • gr8 anwer 我尝试了两次,但两次都出错了......这似乎是一个干净的解决方案
  • 忽略了只计算列表中出现的次数是如此简单,使用Distinct是关键。
【解决方案2】:

你可以使用Dictionary&lt;string, int&gt;,键是字符串,值是计数:

Dictionary<string, int> itemCounts = new Dictionary<string,int>();
for(int i = 0; i < stringLists.Length; i++)
{
    List<string> list = stringLists[i];
    foreach(string str in list.Distinct())
    {
        if(itemCounts.ContainsKey(str))
           itemCounts[str] += 1;
        else
            itemCounts.Add(str, 1);
    }
}
var result = itemCounts.Where(kv => kv.Value >= 2);

我使用list.Distinct(),因为您只想计算不同列表中的出现次数。

根据要求,这里有一个扩展方法,您可以对任何类型重复使用:

public static IEnumerable<T> GetItemsWhichOccurAtLeastIn<T>(this IEnumerable<IEnumerable<T>> seq, int minCount, IEqualityComparer<T> comparer = null)
{
    if (comparer == null) comparer = EqualityComparer<T>.Default;
    Dictionary<T, int> itemCounts = new Dictionary<T, int>(comparer);

    foreach (IEnumerable<T> subSeq in seq)
    {
        foreach (T x in subSeq.Distinct(comparer))
        {
            if (itemCounts.ContainsKey(x))
                itemCounts[x] += 1;
            else
                itemCounts.Add(x, 1);
        }
    }
    foreach(var kv in itemCounts.Where(kv => kv.Value >= minCount))
        yield return kv.Key;
}

用法很简单:

string result = String.Join(",", stringLists.GetItemsWhichOccurAtLeastIn(2)); // a,b,c,d

【讨论】:

  • 我也会用一个不错的扩展方法来包装它。
  • @YuvalItzchakov:根据要求 :)
  • 很好,但为什么要一个 for 循环而不是第二个 foreach 循环?
  • @sloth:你说得对,我可以接受IEnumerable&lt;IEnumerable&lt;T&gt;&gt; 而不是IList。由于Distinct,因此不需要索引。我的第一种方法是处理索引以不计算同一集合中的项目两次。 已编辑
【解决方案3】:

按照以下步骤操作:

  1. 创建字典元素 -> 索引列表
  2. 遍历所有列表
  3. 对于列表编号 i:foreach 列表中的元素:将 i 添加到字典中的列表位置:dictionary[element].Add(i)(如果尚未存在)
  4. 计算字典中有多少个列表有两个条目

【讨论】:

    【解决方案4】:

    您可以使用SelectMany 来展平列表,然后选择所有出现两次或更多次的元素:

    var singleList = stringLists.SelectMany(p => p);
    var results = singleList.Where(p => singleList.Count(q => p == q) >= 2).Distinct();
    

    【讨论】:

    • 太棒了!让我们对迟到 30 秒发布答案的人投反对票 :-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-16
    • 2017-06-06
    • 2020-05-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多