在字符串列表上合并两个相同对象的列表
本文关键字:列表 两个 对象 字符串 合并 | 更新日期: 2023-09-27 18:05:14
我有一个对象Object1
和这个对象的两个列表:
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
上。(此列表中的每个字符串组合应该只出现一次。)如果同一个Parameters
同时存在于List1和List2中,那么List1中的Time
应该被覆盖在List2中。
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();
我的解决方案使用LINQ和一个自定义Comparer实例来比较Parameters成员,并对Parameters中的项做一些假设。如果这些假设是正确的,那么您可以通过使用Param属性来简化实现,就像Max的答案中建议的那样。以下是要点:
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;
}
}
最后,下面是合并列表的实现:
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();
}
像这样修改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();
马克思