如果特定字符串使用linq匹配条件,如何将字符串列表拆分为另一个列表?

时间:2011-01-13 07:02:51

标签: c# linq

我的列表包含{a,b,c,at,h,c,bt} 我想分成List<List<string>>{{a,b,c},{at,h,c},{bt}}; 如果特定字符串包含"t"我需要打破该行,我该如何在linq中执行此操作?

4 个答案:

答案 0 :(得分:4)

嗯,有一种可怕的方式:

int tCounter = 0;
var groups = sequence.GroupBy(x => x.Contains("t") ? ++tCounter : tCounter)
                     .Select(group => group.ToList())
                     .ToList();

或等效(但没有调用Select):

int tCounter = 0;
var groups = sequence.GroupBy(x => x.Contains("t") ? ++tCounter : tCounter,
                              (count, group) => group.ToList())
                     .ToList();

这依赖于GroupBy条款中的副作用 - 这是一个非常糟糕的主意。 LINQ是围绕功能理想设计的,其中查询不应具有副作用。您将副作用放在使用查询的代码中,而不是在查询本身中。这样可行,但我不建议。

这是一个简短而完整的演示,只是为了证明它确实有效:

using System;
using System.Collections.Generic;
using System.Linq;

public class Test
{
    static void Main(string[] args)
    {
        var input = new List<string>{"a","b","c","at","h","c","bt"};

        int tCounter = 0;
        var groups = input.GroupBy(x => x.Contains("t") ? ++tCounter : tCounter)
                          .Select(group => group.ToList())
                          .ToList();
        foreach (var list in groups)
        {
            Console.WriteLine(string.Join(", ", list));
        }
    }
}

输出:

a, b, c
at, h, c
bt

我们真正需要的是“扫描”(又名foldl,我相信 - 不确定)运营商 - 如聚合,但提供正在运行的聚合。然后扫描可以跟踪当前Ts的数量以及当前值,GroupBy可以对此进行处理。

编写这样的操作符并不难,而IIRC是Reactive Extensions System.Interactive assembly已经包含了一个。您可能想要使用它而不是我可怕的笨蛋黑客。那时你可以在LINQ中合理地写出它。

答案 1 :(得分:2)

内置的扩展方法Aggregate正是您所需要的。

var source = new List<string> { "a", "b", "c", "at", "h", "c", "bt" };
var result = source.Aggregate(new List<List<string>>(), (list, s) =>
    {
        if (list.Count == 0 || s.Contains('t')) list.Add(new List<string>());
        list.Last().Add(s);
        return list;
    });

resultList<List<string>>

答案 2 :(得分:1)

我不认为可以使用内置的Linq方法 (实际上,它可以......看到其他答案),但你可以很容易为此目的创建自己的扩展方法:

public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> source, Func<T, bool> isSeparator)
{
    List<T> list = new List<T>();
    foreach(T item in source)
    {
        if (isSeparator(item))
        {
            if (list.Count > 0)
            {
                yield return list;
                list = new List<T>();
            }
        }
        list.Add(item);
    }

    if (list.Count > 0)
    {
        yield return list;
    }
}

使用如下:

var list = new[] { "a", "b", "c", "at", "h", "c", "bt" };
var result = list.Split(s => s.Contains("t"));

答案 3 :(得分:0)

这个问题不会让LINQ尖叫。如果你要求LINQ答案作为心理练习,那就是别的,但这就是我如何解决它(使用一个普通的循环):

        List<List<string>> list = new List<List<string>>();
        List<string> sublist = new List<string>();
        foreach (string element in originalList)
        {
            if (element.Contains("t"))
            {
                list.Add(sublist);
                sublist = new List<string>();
            }
            sublist.Add(element);
        }
        list.Add(sublist);

不要误会我的意思,我滥用LINQ比任何人都多。 :)