如何使用LINQ查找匹配列表中某些条件的范围

时间:2014-07-28 18:18:29

标签: c# linq

这可能有点难以描述,但我正在构建一些统计模型,我需要Linq语法的帮助...我的类比并不完全是我正在做的事情但它是我能来的最简单的描述...... ...

我有一个"命令"输入列表...我们假设我在一个范围内有一个不确定的Int列表......这个列表可能包含1到几百万个实体......

List<int> = [1, 3, 15, 16, 4, 27, 65, 2, 99, 3, 16, 21, 72, 1, 5, 7, 2, 8... ] (range 1 - 100).

我想要推断的是&#34;所有&#34;包含特定&#34;搜索&#34;的范围设置(或子列表)实体。每个子列表必须包含&#34; all&#34;来自原始输入列表中的实体,即维护内部错误数据,并且不得更改顺序...例如,如果我想搜索&#34; all&#34;从上面的列表中包含[1,2,3,4]的范围我应该

  • [1, 3, 15, 16, 4, 27, 65, 2] - 包含搜索列表联合的第一个子列表。
  • [3, 15, 16, 4, 27, 65, 2, 99, 3, 16, 21, 72, 1] - 下一个清单......
  • [4, 27, 65, 2, 99, 3, 16, 21, 72, 1] - 下一个清单......等......

真正关键的信息是&#34;开始和结束指数&#34;每个列表...这些数据需要存储在神经网络中用作矢量数据......使用这些数据,NN可以简单地使用索引对象来进行关键数据计算......

经过一番评估后,我意识到显然每个列表都会以搜索实体开头和结尾。这导致我从这开始......

var indicies = lc.IntListData
                 .Select((v, i) => new { value = v, index = i })
                 .Where(n => search.Contains(n.value))
                 .ToList();

这大大减少了我的清单,从查看数百万个值的列表到查看数以千计的匿名类型的价值和索引......现在,我认为我需要的是从&#34;标记&#34找到;,第一个&#34; n&#34;匿名类型,直到我至少有一个&#34;值&#34;在列表中......不是吗?然后根据需要简单地使用索引值的最小值和最大值......

对Linq语法的任何帮助都是最有帮助的。

1 个答案:

答案 0 :(得分:1)

LINQ版本(带辅助扩展方法):

如果您愿意接受辅助函数从潜在匹配列表中创建元组,那么您可以将LINQ归结为:

var subranges = matches
    .Tuples(search.Length)
    .Where(t => t.Select(n => n.Value).Distinct().Count() == search.Length)
    .Select(t => new { StartIndex=t.Min(n => n.Index), EndIndex=t.Max(n => n.Index) })
    .Select(r => list.Skip(r.StartIndex).Take(r.EndIndex-r.StartIndex+1))
    ;

Tuples方法是此答案的扩展方法的变体:https://stackoverflow.com/a/577612/209103

public static IEnumerable<IEnumerable<T>> Tuples<T>(this IEnumerable<T> sequence, int nTuple)
{
    if(nTuple <= 0) throw new ArgumentOutOfRangeException("nTuple");

    for(int i = 0; i <= sequence.Count() - nTuple; i++)
        for (int j = i+nTuple; j < sequence.Count(); j++)
            yield return sequence.Skip(i).Take(j-i);
}

请注意Tuples为O(n n),整个解决方案为O(n n * n),因此对于较大的数据集效果不佳。

以下非LINQ版本

非LINQ版本的粗略初稿:

var list = new [] { 1, 3, 15, 16, 4, 27, 65, 2, 99, 3, 16, 21, 72, 1, 5, 7, 2, 8 };
var search = new [] { 1, 2, 3, 4 };

var group = new List<int>();
var start = -1;
for (var i=0; i < list.Length; i++) {
    if (search.Contains(list[i])) {
        if (!group.Any()) {
            start = i;
        }
        if (!group.Contains(list[i])) {
            group.Add(list[i]);
        }
        if (group.Count == search.Length) {
            Console.WriteLine(start+" - "+i);
            group.Clear();
            i = start + 1;
            start = -1;
        }
    }
}

这使用蛮力,但可以使用您首先找到匹配索引的方法进行优化。

更新 - 仅考虑相关指数

进行优化
var list = new [] { 1, 3, 15, 16, 4, 27, 65, 2, 1, 99, 3, 16, 21, 72, 1, 5, 7, 4, 2, 8 };
var search = new [] { 1, 2, 3, 4 };

var matches = list
    .Select((v,i) => new { Value=v, Index=i })
    .Where(n => search.Contains(n.Value))
    .ToList()
    ;

for (var start=0; start < matches.Count(); start++) {
    var group = new List<int>();
    group.Add(matches[start].Value);
    foreach (var match in matches.Skip(start)) {
        if (!group.Contains(match.Value)) {
            group.Add(match.Value);
        }
        if (group.Count == search.Length) {
            Console.WriteLine(matches[start].Index+" - "+match.Index);
            break;
        }
    }
}

这两种方法都是非LINQ。我不知道如何将其转换为LINQ表达式。这显然是一个分组练习(.GroupBy),但我看不出要分组的表达方式。