【问题标题】:Taking element from one of several collections从几个集合之一中获取元素
【发布时间】:2013-06-04 11:17:18
【问题描述】:

假设您有 3 个不同项目的列表。您必须按索引从这些列表之一中返回项目。索引是从 0 到 Count(<all lists items>) 的数字。

例子:

lists 1: 10 items;
lists 2: 5 item;
lists 3: 1 item;

因此,索引应该在 0 到 15 之间。按索引逐项:

0: lists 1, item 0;
1: lists 2, item 0:
2: lists 3, item 0;
3: lists 1, item 1;
4: lists 2, item 1;
5: lists 1, item 2;
6: lists 2, item 2;
7: lists 1, item 3;
8: lists 2, item 3;
9: lists 1, item 4;
10: lists 2, item 4;
11: lists 1, item 5;
12: lists 1, item 6;
13: lists 1, item 7;
14: lists 1, item 8;
15: lists 1, item 9;

(对不起,完整的结果。这是为了我自己的理解检查)。

最好的方法是:

  • 确定要使用哪些列表?
  • 要使用列表中的哪个索引来获取项目?

可能的解决方案:

  1. 将所有列表中的所有项目放入一个大列表中。大列表中的每个项目都应包含:列表索引;列表中的项目(或项目索引);
  2. 当索引到达时,简单地从大列表中返回项目。

【问题讨论】:

  • 所以你想在列表轮换中索引项目。有点像具有不等大小磁盘的 RAID 0?
  • 是的。听起来像 RAID 0。
  • by "列表中的哪个索引用于获取项目?"你的意思是你想把那些小列表的索引转换成大的?还是相反?
  • 小列表的索引和该列表的索引。
  • 这个问题背后的要求是什么?对我来说听起来很奇怪......

标签: c# algorithm collections


【解决方案1】:

这是快速解决方案。您可以传递所有列表并获取它们的枚举数。然后迭代枚举器,同时它们可以返回一些结果

public static IEnumerable<T> EnumerateAll<T>(params IEnumerable<T>[] lists)
{
    var enumerators = lists.Select(l => l.GetEnumerator());

    while (enumerators.Any())
    {
        enumerators = enumerators.Where(e => e.MoveNext());

        foreach (var enumerator in enumerators)
            yield return enumerator.Current;           
    }
}

用法:

List<int> list1 = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
List<int> list2 = new List<int> { 12, 13, 14, 15, 16};
List<int> list3 = new List<int> { 17 };


foreach (int x in EnumerateAll(list1, list2, list3))
    Console.WriteLine(x);

输出

 1 12 17 2 13 3 14 4 15 5 16 6 7 8 9 10

这里是更新的解决方案,感谢@Rawling cmets

public static IEnumerable<T> EnumerateAll<T>(params IEnumerable<T>[] lists)
{
    var enumerators = lists.Select(l => l.GetEnumerator()).ToList();

    while (enumerators.Any())
    {
        enumerators.RemoveAll(e => !e.MoveNext());

        foreach (var enumerator in enumerators)
            yield return enumerator.Current;
    }
}

还有一个提示 - 如果您需要按索引引用元素,那么只需调用 ToList()ToArray()

var items = EnumerateAll(list1, list2, list3).ToList();
var item = items[5];

【讨论】:

  • 非常好。但是,我更愿意将 enumerators 设为一个列表并在其上调用 RemoveAll - 事实上,我认为您最终可能会得到数百个“嵌套”Where 枚举器。
  • 当所有列表都包含相同类型的数据时(如示例中的int),它就可以工作。如何改进这种解决方案以从 int、string 和 bool 列表中返回项目?
  • @MaximKorobov 在这种情况下您不能使用强类型结果,因此intstringbool 的常见类型将是object。因此,只需将方法设为非泛型即可。 IE。删除所有&lt;T&gt; 并改用非通用IEnumerable
  • 当然。那么如何理解是从哪个collection item by index 中获取的呢?我问原因对象然后应该转换为适当的类型。
  • @MaximKorobov 您可以使用dynamic 或使用typeof 检查对象类型。取决于你要对结果做什么
【解决方案2】:

无需枚举lists,只需检查index 是否小于最小列表计数(minCount)乘以列表长度(lists.Length),如果是这种情况,则要检索的值为非常简单,或者从列表中返回索引,不包括具有最小计数的列表 (minLists) 并相应地调整索引:

(当然,这仅在您拥有ILists(或数组)的数组时才有效)

public static T GetByIndex<T>(int index, params IList<T>[] lists){
    var minCount = lists.Min(l => l.Count);
    var minLists = lists.Where(l => l.Count == minCount).ToArray();
    if (index < minCount * lists.Length)
        return lists[index % lists.Length][index / lists.Length];
    else 
        return GetByIndex(index - minCount * minLists.Length, lists.Except(minLists).ToArray());
}

public static void SetByIndex<T>(int index, T val, params IList<T>[] lists){
    var minCount = lists.Min(l => l.Count);
    var minLists = lists.Where(l => l.Count == minCount).ToArray();
    if (index < minCount * lists.Length)
    {
        lists[index % lists.Length][index / lists.Length] = val;
    }
    else 
        SetByIndex(index - minCount * minLists.Length, val, lists.Except(minLists).ToArray());
}

【讨论】:

    【解决方案3】:

    因此,您有 N 个列表 (L0, L1, .. Ln),其中包含项目数量 (N0, N1 .. Nn),并且您希望在“主索引”K 处从该结构返回元素。

    如果 K

    如果 K

    如果 K > N * Min (N0...Nn),您可能需要将 K 减少短列表的长度,并将每个此类列表的 N 减少 1。

    类似的东西,可能你需要通过调试来调整一些值。

    【讨论】:

      猜你喜欢
      • 2021-12-08
      • 1970-01-01
      • 1970-01-01
      • 2016-02-26
      • 2015-03-05
      • 2015-12-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多