我有如下情况
class Person
{
string Name;
int Value;
int Change;
}
List<Person> list1;
List<Person> list2;
我需要将这两个列表合并为一个新的List<Person>
,如果是同一个人,则合并记录应该是该人的名字,列表2中的值,变化的值是列表2的值减去列表1的值。如果没有重复,则变化为0。
我有如下情况
class Person
{
string Name;
int Value;
int Change;
}
List<Person> list1;
List<Person> list2;
我需要将这两个列表合并为一个新的List<Person>
,如果是同一个人,则合并记录应该是该人的名字,列表2中的值,变化的值是列表2的值减去列表1的值。如果没有重复,则变化为0。
您可以使用Linq扩展方法Union轻松地完成此操作。例如:
var mergedList = list1.Union(list2).ToList();
这将返回一个List,其中合并了两个列表并删除了重复项。如果您不像我的示例那样在Union扩展方法中指定比较器,它将使用Person类中的默认Equals和GetHashCode方法。例如,如果您想通过比较他们的名称属性来比较人员,则必须覆盖这些方法以进行自己的比较。请检查以下代码示例以完成此操作。您必须将此代码添加到您的Person类中。
/// <summary>
/// Checks if the provided object is equal to the current Person
/// </summary>
/// <param name="obj">Object to compare to the current Person</param>
/// <returns>True if equal, false if not</returns>
public override bool Equals(object obj)
{
// Try to cast the object to compare to to be a Person
var person = obj as Person;
return Equals(person);
}
/// <summary>
/// Returns an identifier for this instance
/// </summary>
public override int GetHashCode()
{
return Name.GetHashCode();
}
/// <summary>
/// Checks if the provided Person is equal to the current Person
/// </summary>
/// <param name="personToCompareTo">Person to compare to the current person</param>
/// <returns>True if equal, false if not</returns>
public bool Equals(Person personToCompareTo)
{
// Check if person is being compared to a non person. In that case always return false.
if (personToCompareTo == null) return false;
// If the person to compare to does not have a Name assigned yet, we can't define if it's the same. Return false.
if (string.IsNullOrEmpty(personToCompareTo.Name) return false;
// Check if both person objects contain the same Name. In that case they're assumed equal.
return Name.Equals(personToCompareTo.Name);
}
Union
和 Intersect
搞混了? - KosConcat
。 - Kos我注意到这个问题在2年后还没有被标记为已回答 - 我认为最接近的答案是Richards,但可以将其简化成这样:
list1.Concat(list2)
.ToLookup(p => p.Name)
.Select(g => g.Aggregate((p1, p2) => new Person
{
Name = p1.Name,
Value = p1.Value,
Change = p2.Value - p1.Value
}));
尽管在任何一个集合中有重复的名称时,这不会导致错误。
其他一些答案建议使用union操作-这绝对不是正确的方法,因为它只会给你一个不同的列表,而不进行组合。
为什么不直接使用 Concat
呢?
Concat
是 Linq 的一部分,比使用 AddRange()
更高效。
在你的情况下:
List<Person> list1 = ...
List<Person> list2 = ...
List<Person> total = list1.Concat(list2);
这是Linq
var mergedList = list1.Union(list2).ToList();
这是正常的(AddRange)
var mergedList=new List<Person>();
mergeList.AddRange(list1);
mergeList.AddRange(list2);
这是正常的 (Foreach)
var mergedList=new List<Person>();
foreach(var item in list1)
{
mergedList.Add(item);
}
foreach(var item in list2)
{
mergedList.Add(item);
}
这是通常的(Foreach-Dublice)
var mergedList=new List<Person>();
foreach(var item in list1)
{
mergedList.Add(item);
}
foreach(var item in list2)
{
if(!mergedList.Contains(item))
{
mergedList.Add(item);
}
}
static class Ext {
public static IEnumerable<T> Append(this IEnumerable<T> source,
IEnumerable<T> second) {
foreach (T t in source) { yield return t; }
foreach (T t in second) { yield return t; }
}
}
var oneList = list1.Append(list2);
然后按名称分组。
var grouped = oneList.Group(p => p.Name);
public Person MergePersonGroup(IGrouping<string, Person> pGroup) {
var l = pGroup.ToList(); // Avoid multiple enumeration.
var first = l.First();
var result = new Person {
Name = first.Name,
Value = first.Value
};
if (l.Count() == 1) {
return result;
} else if (l.Count() == 2) {
result.Change = first.Value - l.Last().Value;
return result;
} else {
throw new ApplicationException("Too many " + result.Name);
}
}
这可以应用于grouped
的每个元素:
var finalResult = grouped.Select(g => MergePersonGroup(g));
(警告:未经测试。)
Append
函数与开箱即用的 Concat
函数几乎完全重复。 - RawlingEnumerable.Concat
,因此不得不重新实现它。 - Richardvar dict1 = list1.ToDictionary(l1 => l1.Name);
var dict2 = list2.ToDictionary(l2 => l2.Name);
//get the full list of names.
var names = dict1.Keys.Union(dict2.Keys).ToList();
//produce results
var result = names
.Select( name =>
{
Person p1 = dict1.ContainsKey(name) ? dict1[name] : null;
Person p2 = dict2.ContainsKey(name) ? dict2[name] : null;
//left only
if (p2 == null)
{
p1.Change = 0;
return p1;
}
//right only
if (p1 == null)
{
p2.Change = 0;
return p2;
}
//both
p2.Change = p2.Value - p1.Value;
return p2;
}).ToList();
public class Person
{
public string Name { get; set; }
public int Value { get; set; }
public int Change { get; set; }
public Person(string name, int value)
{
Name = name;
Value = value;
Change = 0;
}
}
class Program
{
static void Main(string[] args)
{
List<Person> list1 = new List<Person>
{
new Person("a", 1),
new Person("b", 2),
new Person("c", 3),
new Person("d", 4)
};
List<Person> list2 = new List<Person>
{
new Person("a", 4),
new Person("b", 5),
new Person("e", 6),
new Person("f", 7)
};
List<Person> list3 = list2.ToList();
foreach (var person in list1)
{
var existingPerson = list3.FirstOrDefault(x => x.Name == person.Name);
if (existingPerson != null)
{
existingPerson.Change = existingPerson.Value - person.Value;
}
else
{
list3.Add(person);
}
}
foreach (var person in list3)
{
Console.WriteLine("{0} {1} {2} ", person.Name,person.Value,person.Change);
}
Console.Read();
}
}
public void Linq95()
{
List<Customer> customers = GetCustomerList();
List<Product> products = GetProductList();
var customerNames =
from c in customers
select c.CompanyName;
var productNames =
from p in products
select p.ProductName;
var allNames = customerNames.Concat(productNames);
Console.WriteLine("Customer and product names:");
foreach (var n in allNames)
{
Console.WriteLine(n);
}
}