如何从列表中创建新列表,其中元素在新列表中的顺序是另一种顺序?

时间:2017-11-23 07:05:33

标签: c# linq

假设我有列表清单。我想从给定的列表列表中创建新列表,以便元素按照下面给出的示例顺序。

输入: -

List<List<int>> l = new List<List<int>>();

List<int> a = new List<int>();
a.Add(1);
a.Add(2);
a.Add(3);
a.Add(4);
List<int> b = new List<int>();
b.Add(11);
b.Add(12);
b.Add(13);
b.Add(14);
b.Add(15);
b.Add(16);
b.Add(17);
b.Add(18);

l.Add(a);
l.Add(b);

输出(列表): -

1
11
2
12
3
13
4
14
15
16 

输出列表必须是not contain more than 10个元素。

我目前正在使用foreach inside while执行此操作,但我想知道如何使用LINQ执行此操作。

int loopCounter = 0,index=0;
List<int> o=new List<int>();
while(o.Count<10)
{
    foreach(List<int> x in l)
    {
        if(o.Count<10)
           o.Add(x[index]);
    }
    index++;
}

感谢。

3 个答案:

答案 0 :(得分:3)

使用接收项目索引的SelectManySelect重载。这将用于应用所需的顺序。使用SelectMany是为了展平嵌套集合级别。最后,应用Take仅检索所需数量的项目:

var result = l.SelectMany((nested, index) => 
                  nested.Select((item, nestedIndex) => (index, nestedIndex, item)))
              .OrderBy(i => i.nestedIndex)
              .ThenBy(i => i.index)
              .Select(i => i.item)
              .Take(10);

或者在查询语法中:

var result = (from c in l.Select((nestedCollection, index) => (nestedCollection, index))
              from i in c.nestedCollection.Select((item, index) => (item, index))
              orderby i.index, c.index
              select i.item).Take(10);

如果使用C#6.0和之前的项目,则使用匿名类型:

var result = l.SelectMany((nested, index) => 
                  nested.Select((item, nestedIndex) => new {index, nestedIndex, item}))
              .OrderBy(i => i.nestedIndex)
              .ThenBy(i => i.index)
              .Select(i => i.item)
              .Take(10);

解释为什么单独Zip是不够的:zip相当于对第一个集合执行join操作到第一个集合,其中 要加入的属性是索引。因此,只有第一个集合中存在的项目(如果它们在第二个集合中匹配)将显示在结果中。

下一个选项是考虑left join,它将返回第一个集合的所有项目,并在第二个集合中匹配(如果存在)。在描述的情况下,OP正在寻找full outer join的功能 - 尽可能获取收集和匹配的所有项目。

答案 1 :(得分:2)

我知道你要求LINQ,但我经常觉得LINQ是一个锤子,一旦开发人员发现它,每个问题都是钉子。为了可读性/可维护性的观点,我不会用LINQ完成这个,因为我觉得这样的事情更简单,更容易理解/更多自我记录:

List<int> r = new List<int>(10);
for(int i = 0; i < 10; i++){
  if(i < a.Count)
    r.Add(a[i]);
  if(i < b.Count)
    r.Add(b[i]);
}

如果a和b总共只有8个项目,你就不需要提前停止循环,但你可以通过扩展for循环的测试

我也认为这个案例可能比LINQ更具性能,因为它做得少很多

如果您使用LINQ的授权是学术性的(这是必须使用LINQ的作业),那么请继续,但如果它是一个普通的日常系统,其他一些可怜的傻瓜将不得不维持一天,我恳请你考虑这是否是一个很好的LINQ应用程序

答案 2 :(得分:0)

这将处理2个或更多内部List<List<int>> - 它会通过IEnumerable<int>返回yield,因此您必须在其上调用.ToList()才能进行此操作一个列表。 Linq.Any用于休息标准。

将抛出任何列表为null。根据自己的喜好添加支票。

static IEnumerable<int> FlattenZip (List<List<int>> ienum, int maxLength = int.MaxValue)
{
  int done = 0;
  int index = 0;
  int yielded = 0;

  while (yielded <= maxLength && ienum.Any (list => index < list.Count))
    foreach (var l in ienum)
    {
      done++;

      if (index < l.Count)
      {
        // this list is big enough, we will take one out
        yielded++;
        yield return l[index];
      }

      if (yielded > maxLength)
        break; // we are done

      if (done % (ienum.Count) == 0)
        index += 1; // checked all lists, advancing index
    }
}

public static void Main ()
{
  // other testcases to consider: 
  //   in total too few elememts
  //   one list empty (but not null)
  //   too many lists (11 for 10 elements) 

  var l1 = new List<int> { 1, 2, 3, 4 };
  var l2 = new List<int> { 11, 12, 13, 14, 15, 16 };
  var l3 = new List<int> { 21, 22, 23, 24, 25, 26 };

  var l = new List<List<int>> { l1, l2, l3 };

  var zipped = FlattenZip (l, 10);

  Console.WriteLine (string.Join (", ", zipped));
  Console.ReadLine ();
}