38

我有三个(可能有超过 3-4 个通用列表,但在这个例子中让 3 个)通用列表。

List<string> list1

List<string> list2

List<string> list3

所有列表都具有相同数量的元素(相同的计数)。

我用它来将两个列表与 ZIP 结合起来:

var result = list1.Zip(list2, (a, b) => new {
  test1 = f,
  test2 = b
}

我用那个 forforeach语句来避免foreach每个列表,比如

foreach(var item in result){
Console.WriteLine(item.test1 + " " + item.test2);
}

如何在三个列表中使用与 Zip 相似的方法?

谢谢

编辑:

我想喜欢:

List<string> list1 = new List<string>{"test", "otherTest"};

List<string> list2 = new List<string>{"item", "otherItem"};

List<string> list3 = new List<string>{"value", "otherValue"};

ZIP 之后(我不知道方法),我想要结果(在 VS2010 调试模式下)

[0] { a = {"test"},
      b = {"item"},
      c = {"value"}
    }   

[1] { a = {"otherTest"},
      b = {"otherItem"},
      c = {"otherValue"}
    }  

怎么做 ?

4

7 回答 7

46

对我来说最明显的方法是使用Zip两次。

例如,

var results = l1.Zip(l2, (x, y) => x + y).Zip(l3, (x, y) => x + y);

将组合(添加)三个List<int>对象的元素。

更新:

您可以定义一个新的扩展方法,其作用类似于Zip带有三个IEnumerables 的 a,如下所示:

public static class MyFunkyExtensions
{
    public static IEnumerable<TResult> ZipThree<T1, T2, T3, TResult>(
        this IEnumerable<T1> source,
        IEnumerable<T2> second,
        IEnumerable<T3> third,
        Func<T1, T2, T3, TResult> func)
    {
        using (var e1 = source.GetEnumerator())
        using (var e2 = second.GetEnumerator())
        using (var e3 = third.GetEnumerator())
        {
            while (e1.MoveNext() && e2.MoveNext() && e3.MoveNext())
                yield return func(e1.Current, e2.Current, e3.Current);
        }
    }
}

用法(在与上述相同的上下文中)现在变为:

var results = l1.ZipThree(l2, l3, (x, y, z) => x + y + z);

同样,您的三个列表现在可以与:

var results = list1.ZipThree(list2, list3, (a, b, c) => new { a, b, c });
于 2012-04-24T11:42:58.320 回答
5

我知道还有另一个非常有趣的解决方案。从教育的角度来看,这很有趣,但如果需要执行压缩不同数量的列表 A LOT,那么它也可能有用。

此方法覆盖 .NET 的 LINQSelectMany函数,当您使用 LINQ 的查询语法时,该函数由约定采用。标准SelectMany实现执行笛卡尔积。被覆盖的可以进行压缩。实际的实现可能是:

static IEnumerable<TResult> SelectMany<TSource, TCollection, TResult>(this IEnumerable<TSource> source,
        Func<TSource, IEnumerable<TCollection>> selector, Func<TSource, TCollection, TResult> select)
{
    using (var e1 = source.GetEnumerator())
        using (var e2 = selector(default(TSource)).GetEnumerator())
            while (true)
                if (e1.MoveNext() && e2.MoveNext())
                    yield return select(e1.Current, e2.Current);
                else
                    yield break;
}

它看起来有点吓人,但它是一种压缩逻辑,如果编写一次,可以在很多地方使用,并且客户端的代码看起来很漂亮——你可以IEnumerable<T>使用标准 LINQ 查询语法压缩任意数量的代码:

var titles = new string[] { "Analyst", "Consultant", "Supervisor"};
var names = new string[] { "Adam", "Eve", "Michelle" };
var surnames = new string[] { "First", "Second", "Third" };

var results =
    from title in titles
    from name in names
    from surname in surnames
    select $"{ title } { name } { surname }";

如果你然后执行:

foreach (var result in results)
    Console.WriteLine(result);

你会得到:

Analyst Adam First
Consultant Eve Second
Supervisor Michelle Third

你应该在你的类中保持这个扩展是私有的,否则你会从根本上改变周围代码的行为。此外,一种新类型将很有用,这样它就不会与 IEnumerables 的标准 LINQ 行为发生冲突。

出于教育目的,我使用此扩展方法创建了一个小型 c# 项目 + 一些好处:https ://github.com/lukiasz/Zippable

此外,如果您觉得这很有趣,我强烈推荐Jon Skeet 的 Reimplementing LINQ to Objects 文章

玩得开心!

于 2017-08-19T17:19:29.350 回答
3

这是我们需要决定是使用具有更好可读性的代码还是更短的 Linq 代码的情况之一,我更喜欢代码可读性。

class Program
{
    static void Main(string[] args)
    {
        List<string> list1 = new List<string> { "test", "otherTest" };
        List<string> list2 = new List<string> { "item", "otherItem" };
        List<string> list3 = new List<string> { "value", "otherValue" };

        var result = CombineListsByLayers(list1, list2, list3);
    }

    public static List<string>[] CombineListsByLayers(params List<string>[] sourceLists)
    {
        var results = new List<string>[sourceLists[0].Count];

        for (var i = 0; i < results.Length; i++)
        {
            results[i] = new List<string>();
            foreach (var sourceList in sourceLists)
                results[i].Add(sourceList[i]);
        }
        return results;
    }
于 2012-04-24T13:21:04.167 回答
3

您可以将 C# 中的许多列表与级联 zip 方法和匿名类以及元组结果结合起来。

List<string> list1 = new List<string> { "test", "otherTest" };
List<string> list2 = new List<string> { "item", "otherItem" };
List<string> list3 = new List<string> { "value", "otherValue" };

IEnumerable<Tuple<string, string, string>> result = list1
    .Zip(list2, (e1, e2) => new {e1, e2})
    .Zip(list3, (z1, e3) => Tuple.Create(z1.e1, z1.e2, e3));

结果是:

[0]
{(test, item, value)}
    Item1: "test"
    Item2: "item"
    Item3: "value"
于 2016-08-17T06:40:55.437 回答
2

用于压缩任意数量的不同大小列表的通用解决方案:

public static IEnumerable<TItem> ZipAll<TItem>(this IReadOnlyCollection<IEnumerable<TItem>> enumerables)
{
   var enumerators = enumerables.Select(enumerable => enumerable.GetEnumerator()).ToList();
   bool anyHit;
   do
   {
      anyHit = false;
      foreach (var enumerator in enumerators.Where(enumerator => enumerator.MoveNext()))
      {
          anyHit = true;
          yield return enumerator.Current;
      }
   } while (anyHit);

   foreach (var enumerator in enumerators)
   {
      enumerator.Dispose();
   }
}
于 2020-01-28T09:42:35.087 回答
0

您可以将这些组合List<string>List<List<string>>并聚合它

List<string> list1 = new List<string> { "test", "otherTest" };
List<string> list2 = new List<string> { "item", "otherItem" };
List<string> list3 = new List<string> { "value", "otherValue" };

var list = new List<List<string>>() { list1, list2, list3 }
    .Aggregate(
        Enumerable.Range(0, list1.Count).Select(e => new List<string>()),
        (prev, next) => prev.Zip(next, (first, second) => { first.Add(second); return first; })
    )
    .Select(e => new
    {
        a = e.ElementAt(0),
        b = e.ElementAt(1),
        c = e.ElementAt(2)
    });

结果

[
  {
    "a": "test",
    "b": "item",
    "c": "value"
  },
  {
    "a": "otherTest",
    "b": "otherItem",
    "c": "otherValue"
  }
]

参见dotnetfiddle.net

于 2019-07-13T10:22:50.743 回答
0

使用.NET 6

.. 及以上Zip可用于生成包含来自三个指定序列的元素的元组序列。[文档]

var titles = new string[] { "Analyst", "Consultant", "Supervisor"};
var names = new string[] { "Adam", "Eve", "Michelle" };
var surnames = new string[] { "First", "Second", "Third" };

IEnumerable<(string Title, string Name, string Surname)> zip = titles.Zip(names, surnames);
于 2021-11-16T04:55:23.523 回答