2013-03-22 47 views
0

我有一個對象Object1這兩個對象的名單列表上:合併同一對象的2所列出串

public class Object1 
{ 
    public TimeSpan? Time { get; set; } 
    public List<string> Parameters { get; set; } 
} 

List<Object1> List1 = List<Object1>(); 
List<Object1> List2 = List<Object1>(); 

現在,我想這兩個列表合併,但這應該發生在對象內的列表Parameters。 (此列表中的每個字符串組合應該只出現一次。) 如果在List1和List2中都存在相同的Parameters,則List1中的Time應該被List2中的Time覆蓋。

例如:

List1:    List2: 
Parameters Time Parameters Time 
1;1   null 1;1   1:20 
1;2   null 1;2   0:51 
1;3   null 
2;5   0:30 

Result: 
Parameters Time 
1;1   1:20 
1;2   0:51 
1;3   null 
2;5   0:30 

這可能是通過使用一個for循環實現的,但由於列表會變得非常大,它可能不執行好。

我已經試圖通過使用下面的代碼來做到這一點,但這似乎只是連接列表。

var query = List1.Concat(List2) 
.GroupBy(x => x.Parameters) 
.Select(g => g.OrderByDescending(x => x.Time).First()) 
.ToList(); 
+1

它應該做的,如果爲同一值'Parameters'兩個列表中存在?或者如果同一個列表中存在多個值? – 2013-03-22 15:56:32

+0

我想如果不是空的話,使用相同的列表 – 2013-03-22 15:58:28

+1

那麼結果應該排序嗎? (你的嘗試似乎表明它應該是)。如何確定在重複時要接受哪個值?或者沒有關係? – 2013-03-22 15:58:57

回答

1

修改您Object1這樣

public class Object1 
{ 
    public Object1() 
    { 
     this.Parameters = new List<string>(); 
    } 

    public TimeSpan? Time { get; set; } 

    public List<string> Parameters { get; set; } 

    public string Param 
    { 
     get 
     { 
      return string.Join(",", this.Parameters.OrderBy(o => o).ToArray()); 
     } 

    } 
} 

修改您的查詢像這樣

var query = list1.Concat(list2) 
      .GroupBy(x => x.Param) // <- Changed with the new properties 
      .Select(g => g.OrderByDescending(x => x.Time).First()) 
      .ToList(); 

最大

+0

非常好,這工作(除了我遺漏'.OrderBy(o => o)'因爲這重新排列'Parameters'列表,導致1; 2看起來像2; 1) 謝謝:) – Yves 2013-03-22 17:45:51

2

我的解決方案使用LINQ和一個自定義比較實例進行比較的參數成員並對參數中的項目做了一些假設。但是,如果這些假設是正確的,那麼可以使用像Max's answer中建議的那樣的Param屬性來簡化實現。這是它的要點:

private static List<Object1> MergeLists(List<Object1> list1, List<Object1> list2) 
{ 
    var parameterComparer = new ParameterComparer(); 

    var distinctParameters = list1.Select(o => o.Parameters) 
     .Concat(list2.Select(o => o.Parameters)) 
     .Distinct(parameterComparer); 

    return (from p in distinctParameters 
      let o1 = list1.SingleOrDefault(o => parameterComparer.Equals(p, o.Parameters)) 
      let o2 = list2.SingleOrDefault(o => parameterComparer.Equals(p, o.Parameters)) 
      let result = o2 ?? o1 
      select result).ToList(); 
} 

這是一個更全面的測試驅動的答案。首先,Object1聲明 - 我附加了輔助構造,使聲明更加簡潔:

public class Object1 
{ 
    public TimeSpan? Time { get; set; } 
    public List<string> Parameters { get; set; } 

    public Object1(TimeSpan? time, params string[] parameters) 
    { 
     Time = time; 
     Parameters = parameters.ToList(); 
    } 
} 

接下來,TestMethod的。我定義了Object1Comparer以使測試的實現更簡單 - 解決方案不需要它。

[TestMethod] 
public void MergeListsTest() 
{ 
    // Arrange 
    var list1 = new List<Object1> 
        { 
         new Object1(null, "1", "1"), 
         new Object1(null, "1", "2"), 
         new Object1(null, "1", "3"), 
         new Object1(new TimeSpan(0, 0, 30), "2", "5") 
        }; 
    var list2 = new List<Object1> 
        { 
         new Object1(new TimeSpan(0, 1, 20), "1", "1"), 
         new Object1(new TimeSpan(0, 0, 51), "1", "2"), 
        }; 
    var expected = new List<Object1> 
         { 
          new Object1(new TimeSpan(0, 1, 20), "1", "1"), 
          new Object1(new TimeSpan(0, 0, 51), "1", "2"), 
          new Object1(null, "1", "3"), 
          new Object1(new TimeSpan(0, 0, 30), "2", "5") 
         }; 

    // Act 
    List<Object1> actual = MergeLists(list1, list2); 

    // Assert 
    // Note: need to order the actual result to use CollectionAssert.AreEqual() 
    List<Object1> orderedActual = actual.OrderBy(o => string.Join(";", o.Parameters)).ToList(); 
    CollectionAssert.AreEqual(expected, orderedActual, new Object1Comparer()); 
} 

public class Object1Comparer : IComparer, IComparer<Object1> 
{ 
    public int Compare(Object1 x, Object1 y) 
    { 
     if (x.Time == null && y.Time == null) return 0; 
     if (x.Time == null || y.Time == null) return -1; 
     int timeComparison = TimeSpan.Compare(x.Time.Value, y.Time.Value); 
     if (timeComparison != 0) return timeComparison; 

     if (x.Parameters == null && y.Parameters == null) return 0; 
     if (x.Parameters == null || y.Parameters == null) return -1; 
     if (x.Parameters.SequenceEqual(y.Parameters)) return 0; 
     return -1; 
    } 

    public int Compare(object x, object y) 
    { 
     if (x is Object1 && y is Object1) 
      return Compare(x as Object1, y as Object1); 
     return -1; 
    } 
} 

最後,這裏的MergeLists執行:

public class ParameterComparer : IEqualityComparer<List<string>> 
{ 
    public bool Equals(List<string> x, List<string> y) 
    { 
     if (x == null && y == null) return true; 
     if (x == null || y == null) return false; 

     return x.SequenceEqual(y); 
    } 

    public int GetHashCode(List<string> obj) 
    { 
     if (obj == null) throw new ArgumentNullException("obj"); 

     // Note: this is not a safe way to get a hash code, 
     // but if you're sure that the members are always ordered 
     // and will never contain a semi-colon, then it will work. 
     return string.Join(";", obj).GetHashCode(); 
    } 
} 

private static List<Object1> MergeLists(List<Object1> list1, List<Object1> list2) 
{ 
    var parameterComparer = new ParameterComparer(); 

    var distinctParameters = list1.Select(o => o.Parameters) 
     .Concat(list2.Select(o => o.Parameters)) 
     .Distinct(parameterComparer); 

    return (from p in distinctParameters 
      let o1 = list1.SingleOrDefault(o => parameterComparer.Equals(p, o.Parameters)) 
      let o2 = list2.SingleOrDefault(o => parameterComparer.Equals(p, o.Parameters)) 
      let result = o2 ?? o1 
      select result).ToList(); 
} 
+0

+1對於實際寫作這麼多! – 2013-03-22 17:24:12

+0

一個不錯的解決方案,但也許有點過於複雜,謝謝:) – Yves 2013-03-22 17:54:13