【问题标题】:How to handle duplicate keys while joining two lists?加入两个列表时如何处理重复键?
【发布时间】:2014-02-21 04:11:23
【问题描述】:

我是 C# 新手。

我有以下结构。

struct Foo
{
    string key;
    Bar values;
}

我有两个 Foo 列表,大小相等的 L1 和 L2 都包含相同的键集。

我必须合并 L1 和 L2 中对应的 Foo 实例。

Foo Merge(Foo f1, Foo f2)
{
   // merge f1 and f2.
   return result.
}

我写了以下内容来实现这一点。

resultList = L1.Join(L2, f1 => f1.key, f2 => f2.key, (f1, f2) => Merge(f1, f2)
                        ).ToList())

我的问题是我的密钥不是唯一的。我在 L1 中有 n 个元素具有相同的键(比如“key1”)(它们也出现在 L2 的某个地方)。因此,上面的连接语句为 L1 中的每个“key1”从 L2 中选择 n 个匹配条目,并且在我只想要 n 的结果中得到带有键“key1”的 n*n 个元素。 (因此,这是这些元素集的一种叉积)。

我想使用 Join 并仍然使用“key1”从 L1 中选择一个元素,并强制 Linq 使用 L2 中第一个可用的“未使用”“key1”元素。这可能吗?在这里加入是个坏主意吗?

(另外,我想保留 L1 中的键顺序。我尝试在连接之前使用这些键处理所有元素,并从 L1 和 L2 中删除这些条目。这扰乱了键的顺序,它看起来很丑)。

我正在寻找一个没有任何明确 for 循环的解决方案。

【问题讨论】:

  • 第一个列表中的所有键都在第二个列表中,但顺序不同,还是更复杂?
  • 就更复杂了。重复键多次出现。
  • 是的,但是如果您在第一个列表中有 3 次“3”作为键,那么在第二个列表中您将有 3 次“3”作为键(但不在同一个位置)?跨度>

标签: c# linq list join


【解决方案1】:

从您对 ElectricRouge 答案的评论中,您可以执行类似的操作

var z = list1.Join(list2.GroupBy(m => m.Id), 
                   m => m.Id, 
                   g => g.Key, 
                   (l1, l2) => new{l1, l2});

这将为您提供 l1 中所有键的列表,以及 l2 中相应的分组键。

不确定它是否真的可读。

【讨论】:

  • 我明白你的意思。我认为这应该足够了。我现在正在尝试并尝试解决有关表达式“list2.GroupBy(m => m.Id)”和“list2”的兼容性的一些编译器问题。我会尽快通知你的。
  • 我无法让它工作,因为 GroupBy 返回一些 IGrouping 列表并且无法从中提取我的结构。因此,我调整了您的想法,将 Distinct() 与自定义比较器一起使用。请看我的回答。
【解决方案2】:

I need to find the corresponding entries in two lists and do some operation on them. That is my preliminary requirement

为此,您可以执行以下操作。

var z=S1.Select(i=>i.Key).Tolist(); //make a list of all keys in S1
List<Foo> result=new List<Foo>();
foreach(var item in z)    // Compare with S2 using keys in z
{
   var x=item.Where(i=>i.Key==item.Key)
   result.Add(x);  
}

这是你要找的吗?

【讨论】:

  • 请看我对上述答案的回答。我不想将这两个列表“一个接一个”。我想将它们“并排”放置,并对匹配的元素做一些事情。
  • 理想情况下,我正在寻找一种没有 for 循环的解决方案。使用 for 循环,我可以简单地遍历第一个列表中的条目,如果我在 S1 中找到一个条目,在 S2 中有多个对应的项目,那么我可以以某种方式处理它。没有for循环有可能吗?
【解决方案3】:

我想使用 Join 并仍然使用“key1”从 L1 中选择一个元素,并强制 Linq 使用 L2 中第一个可用的“未使用”“key1”元素。这可能吗?

当组合两个列表中的元素时,您希望选择第二个列表中的第一个元素,该元素与第一个列表中的元素具有相同的键。 (以前,我对您的问题的解释不同,此答案的编辑历史中提供了针对此不同问题的解决方案。)

为了快速访问第二个列表中的所需值,创建了一个字典,提供从键到第二个列表中所需值的查找:

var dictionary2 = list2
  .GroupBy(foo => foo.Key)
  .ToDictionary(group => group.Key, group => group.First());

First 的使用表示您希望在第二个列表中选择具有相同键的第一个元素

现在通过在第一个列表上使用投影来创建合并列表:

var mergedList = list1.Select(
  foo => Merge(
    foo,
    dictionary2[foo.Key]
  )
);

当您使用foreach 迭代mergedListToList() 时,将计算所需的结果。

【讨论】:

  • 感谢您的回答。这是一种有趣的方法,但有点复杂。事实上,索引只是第一个列表的“虚拟键”的一部分。对于第二个列表,没关系。请查看我自己的答案(改编自 Raphaël 的答案),其中第二个列表被压缩以消除重复项。
  • @PermanentGuest:很好,你解决了你的问题。显然,我误解了 use the first available 'unused' "key1" element from L2 中的术语 unused。其实我还是不明白你说的unused是什么意思。
  • 是的,那句话有点混乱。对此感到抱歉。我想说的是,我想让 list1 中的所有对象按顺序排列,而 list2 中的“对应”对象。现在,如果有重复的键,那么如何为它们定义“对应”?所以,我编造了这个定义。仅考虑重复键。从 list1 中获取第一个这样的键,并使用该键从 list2 中获取第一个可用对象。从 list1 中取出下一个这样的对象时,不要取出上一步中选择的对象(很明显),而是取出下一个(因此“未使用”)。
  • @PermanentGuest:我编辑了我的答案以提供一个解决方案,希望基于对您问题的正确理解。
  • 谢谢,马丁。这应该有效。请参阅 Raphael 和我的答案。它们基本上具有相似的想法(压缩 list2 以避免重复)。
【解决方案4】:

您可以使用 Union 删除重复的密钥。 http://msdn.microsoft.com/en-us/library/bb341731.aspx的文档

List<int> list1 = new List<int> { 1, 12, 12, 5};
List<int> list2 = new List<int> { 12, 5, 7, 9, 1 };
List<int> ulist = list1.Union(list2).ToList();

示例来自:how to merge 2 List<T> with removing duplicate values in C#

或者您可以使用 Concat 合并不同类型的列表(保留所有键)。 查看她的文档:http://msdn.microsoft.com/en-us/library/bb302894(v=vs.110).aspx

var MyCombinedList = Collection1.Concat(Collection2)
                                .Concat(Collection3)
                                .ToList();

来自同一问题的示例:Merge two (or more) lists into one, in C# .NET

【讨论】:

  • 不知道这是否会删除重复的密钥。但是使用 Union 会。见msdn.microsoft.com/en-us/library/bb341731.aspx
  • 我不想一个接一个地附加一个列表。我需要在两个列表中找到对应的条目并对其进行一些操作。这是我的初步要求。我可以在该操作之前删除我的重复条目并单独处理它们。但是,这会丢失我在 L1 中的订单。
  • 以合并列表和删除重复条目为例。示例用于附加列表。
【解决方案5】:

最后我改编了拉斐尔的回答如下。

public class FooComparer : IEqualityComparer<Foo>
{
      public bool Equals(Foo o1, Foo o2)
      {
          return o1.key == o2.key;
      }

      public int GetHashCode(Foo obj)
      {
          return obj.key.GetHashCode();
      }
}


resultList = L1.Join(L2.Select(m => m).Distinct(new FooComparer()).ToList(), f1 => f1.key, f2 => f2.key, (f1, f2) => Merge(f1, f2)
                        ).ToList());

简短说明:

L2.Select(m => m).Distinct(new FooComparer()).ToList()

通过从 L2 中删除重复键来创建一个新列表。将 L1 加入这个新列表以获得所需的结果。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-12-24
    • 2014-10-08
    • 2019-02-19
    • 1970-01-01
    • 2014-10-20
    • 2012-04-27
    • 2021-04-25
    相关资源
    最近更新 更多