2015-11-04 53 views
5

我想问一下是否有一种优雅而有效的方式将两个MyClass列表合并为一个?根据属性将两个列表合并为一个

MyClass的是这样的:

  • ID:int
  • 名称:string
  • EXTID:int?

和列表是根据列表中的不同来源和对象填充做份额ID,所以它看起来像这样:

MyClass instance from List1 
ID = someInt 
Name = someString 
ExtID = null 

而且从列表2

MyClass的实例
ID = someInt (same as List1) 
Name = someString (same as List1) 
ExtID = someInt 

我基本上需要的是这两个清单合并,所以结局是包含一个列表:

ID = someInt (from List1) 
Name = someString (from List1) 
ExtID = someInt (null if no corresponding item - based on ID - on List2) 

我知道我能做到这一点简单地使用foreach循环,但我很想知道是否有更优雅和更可取的(由于性能,可读性)方法?

+0

编写一个'Merge'函数,将两个具有相同'ID'的实例组合成一个 - 然后你可以连接列表,按ID编组并最终使用合并函数折叠/缩小组 - 这就是我将如何做到的为readabi lity - 对于性能来说,你可能在排序和循环时很好 – Carsten

回答

1

有很多方法取决于什么是优先级,例如。联盟+查找:

//this will create a key value pairs: id -> matching instances 
var idMap = list1.Union(list2).ToLookup(myClass => myClass.ID); 
//now just select for each ID the instance you want, ex. with some value 
var mergedInstances = idMap.Select(row => 
     row.FirstOrDefault(myClass => myClass.ExtId.HasValue) ?? row.First()); 

以上的好处是,它会与,即使它们包含许多重复的isntances无论任何金额列出工作,那么你就可以很容易地修改合并

小的提升条件将提取合并实例的方法:

MyClass MergeInstances(IEnumerable<MyClass> instances){ 
    return instances.FirstOrDefault(myClass => myClass.ExtId.HasValue) 
      ?? instances.First(); //or whatever else you imagine 
} 

,现在只用它在上面的代码中

var mergedInstances = idMap.Select(MergeInstances); 

清洁,灵活,简单,无附加条件。性能不明智,但谁在乎。

编辑:由于性能的优先级,多了一些选择

  1. 进行查找像上面但只适用于较小的列表。然后遍历更大的并做所需的更改O(m log m)+ O(n)。 m - 更小的列表大小,n更大的列表大小 - 应该是最快的。

  2. 按元素ID排序这两个列表。创建一个for循环,迭代它们两个,使两个列表的当前索引保持为具有相同id的元素。将索引移动到在这两个列表中找到的下一个最小的标识符,如果只有一个标识符,则只将其移入。O(n log n)+ O(m log m)+ O(n);

+0

它会比List2上的foreach循环更快地运行到List1中的相应项目并设置值? ;>如果没有,那么因为它隐藏在库的私有方法中,所以我不介意使用foreach循环来获得更多的性能,因为我不知道列表可以增长多少,以及这将使用多长时间。我可能已经说错了,但优先考虑的是性能。 – user1970395

+1

如果对于每个元素需要搜索其他列表的元素,那么你会得到O(n^2)的复杂性,因为查找是O(log n)* O(n)Select这实际上是一个foreach循环O(n log n)对O(n ^)。我的胜利;)。无论如何,使用查找表(字典/地图)是一种方法。而且你也获得了灵活性。如果你可以改进现有的解决方案,你可以考虑从头开始提供一个字典,而不是列表。 – mikus

+1

您可以在列表中的一个上创建查找(或字典),然后使用查找快速查找第二个查找。 你也可以考虑订购这两个清单,并做一个智能循环,只有一次通过两个清单O(n log n)+ O(n)。 – mikus

-1

我sugest建立在类的方法foreach循环,所以每次你需要做你会使用类似

instanceList1.MergeLists(instanceList2) 

,并用这种方法,你可以这样的事情控制合并操作所需的所有内容。

+0

@mikus LINQ * does *使用迭代和迭代器,所以关于'foreach'的注释不适用。另一方面,当OP询问如何编写该函数时,“编写自己的函数”并不是一个好的答案。也许这应该被删除并重新张贴为评论? –

+0

其关于很好的解决方案不是一个不会在内部使用循环的解决方案... – mikus

+0

嗯,我只是假定他知道如何去做这个功能,我认为这应该是更好的方法。 (他说他不想要这样的foreach是的,但我认为他的意思是,他不想每次需要合并时都这样做) –

1

这是你想要的

var joined = from Item1 in list1 
     join Item2 in list2 
     on Item1.Id equals Item2.Id // join on some property 
     select new MyClass(Item1.Id, Item1.Name, Item1.ExtID??Item2.ExtID); 

编辑:如果你正在寻找一个外部联接,

var query = from Item1 in list1 
      join Item2 in list2 on Item1.Id equals Item2.Id into gj 
      from sublist2 in gj.DefaultIfEmpty() 
      select new MyClass(Item1.Id, Item1.Name, sublist2??string.empty); 

可读性明智的,使用foreach循环是不是太糟糕的主意..

+0

因为它是内部连接,如果某些id是缺少其中一个列表 – mikus

+0

yeah..true..added outer join case – Godsent