如何加入两个名单?

时间:2014-09-26 23:56:34

标签: c# linq

(完整代码:https://dotnetfiddle.net/tdKNgH

我有两个由ParentName关联的列表,我想以特定的方式加入它们。

class Parent
{
    public string ParentName { get; set; }
    public IEnumerable<string> ChildNames { get; set; }
}

class Child
{
    public string ParentName { get; set; }
    public string ChildName { get; set; }
}

var parents = new List<Parent>()
{
    new Parent() {ParentName = "Lee"},
    new Parent() {ParentName = "Bob"},
    new Parent() {ParentName = "Tom"}
};

var children = new List<Child>()
{
    new Child() {ParentName = "Lee", ChildName = "A"},
    new Child() {ParentName = "Tom", ChildName = "B"},
    new Child() {ParentName = "Tom", ChildName = "C"}
};

我正在使用foreach循环加入,并且它有效,但有更简洁的方法吗?

foreach (var parent in parents)
{
    var p = parent; // to avoid foreach closure side-effects
    p.ChildNames = children.Where(c => c.ParentName == p.ParentName)
                           .Select(c => c.ChildName);
}

以下是生成的父母列表的样子:

Parent Children
------ --------
Lee    A 
Bob    (empty) 
Tom    B,C

5 个答案:

答案 0 :(得分:1)

您可以为枚举添加扩展方法:

public static void Each<T>(this IEnumerable<T> source, Action<T> action)
{
    if (action == null)
        return;
    foreach (T obj in source)
        action(obj);
}

然后做:

parents.Each(p => p.ChildNames = children.Where(c => c.ParentName == p.ParentName)
                                         .Select(c => c.ChildName));

答案 1 :(得分:1)

您可以进行群组加入。但LINQ并不打算更新。所以我不确定这是否会让你真正有用。

IEnumerable<Parent> parents = ...;

var parentsWithChildren = parents.GroupJoin(children,
                                            c => c.ParentName,
                                            c => c.ParentName,
                                            (a, b) => new
                                                      {
                                                          Parent = a,
                                                          ChildNames = b.Select(x => x.ChildName)
                                                      });

foreach (var v in parentsWithChildren)
{
    v.Parent.ChildNames = v.ChildNames;
}

如果给你的所有人都是父名称和孩子,而不是完整的Parent对象,那肯定会有所帮助,因为那样你就可以将这个集合加入到子名中,并且创建父项的实例,我创建一个匿名类型((a, b) => new { ... })。但是,因为我假设您的Parent对象实际上不仅仅包含一个名称而且这只是一个例子,这似乎是您最好的选择。

答案 2 :(得分:1)

考虑将父母的姓名称为Parent.Name而不是Parent.ParentName(父母的父母?),Child有同样的问题......

class Parent
{
    public string Name { get; set; }
    public IEnumerable<string> ChildrenNames { get; set; }
}

class Child
{
    public string ParentName { get; set; }
    public string Name { get; set; }
}

您可以先通过创建foreach数组完全避免parentNames

var parentNames = new[] { "Lee", "Bob", "Tom" };
var allChildren = new List<Child>()
{
    new Child() {ParentName = "Lee", Name = "A"},
    new Child() {ParentName = "Tom", Name = "B"},
    new Child() {ParentName = "Tom", Name = "C"}
};

这样父母完全由LINQ构建而没有任何副作用(没有更新任何变量),它应该非常简单:

var parents =
    from parentName in parentNames
    join child in allChildren on parentName equals child.ParentName into children
    select new Parent { Name = parentName, ChildrenNames = children.Select(c => c.Name) };

答案 3 :(得分:1)

鉴于LINQ基于功能原则,副作用通常是一个很大的禁忌(也是为什么没有foreach方法的原因)。

因此,我建议采用以下解决方案:

var parents = new List<Parent>()
{
    new Parent() { ParentName = "Lee" },
    new Parent() { ParentName = "Bob" },
    new Parent() { ParentName = "Tom" }
};

var children = new List<Child>()
{
    new Child() { ParentName = "Lee", ChildName = "A" },
    new Child() { ParentName = "Tom", ChildName = "B" },
    new Child() { ParentName = "Tom", ChildName = "C" }
};

var parentsWithChildren = parents.Select(x => new Parent 
{ 
    ParentName = x.ParentName, 
    ChildNames = children
        .Where(c => c.ParentName == x.ParentName)
        .Select(c => c.ChildName) 
});

foreach (var parent in parentsWithChildren)
{
    var childNamesConcentrated = string.Join(",", parent.ChildNames);

    var childNames = string.IsNullOrWhiteSpace(childNamesConcentrated) 
        ? "(empty)" : childNamesConcentrated;

    Console.WriteLine("Parent = {0}, Children = {1}", parent.ParentName, childNames);
}

上述解决方案,通过设置Parent修改集合parents的{​​{1}}个对象。相反,它会创建一组新的ChildNames s及其各自的ChildNames。

答案 4 :(得分:1)

您可以使用ToLookup获得最佳性能并减少内存损失:

 var clu = children.ToLookup(x => x.ParentName, x => x.ChildName);
 parents.ForEach(p => p.ChildNames = clu[p.ParentName]);