10

我想要一种可以拆分IEnumerable谓词的方法,通过它们相对于谓词的索引将项目组合在一起。例如,它可以List<string>在满足x => MyRegex.Match(x).Success的项目中拆分 a,将“介于”这些匹配项之间的项目组合在一起。

它的签名看起来有点像

public static IEnumerable<IEnumerable<TSource>> Split<TSource>(
    this IEnumerable<TSource> source,
    Func<TSource, bool> predicate,
    int bool count
)

,可能带有包含所有分隔符的输出的额外元素。

有没有比foreach循环更有效和/或更紧凑的方法来实现这一点?我觉得应该可以使用 LINQ 方法来实现,但我不能指望它。

例子:

string[] arr = {"One", "Two", "Three", "Nine", "Four", "Seven", "Five"};
arr.Split(x => x.EndsWith("e"));

以下任何一项都可以:

IEnumerable<string> {{}, {"Two"}, {}, {"Four", "Seven"}, {}}
IEnumerable<string> {{"Two"}, {"Four", "Seven"}}

用于存储匹配的可选元素是{"One", "Three", "Nine", "Five"}.

4

5 回答 5

37

如果您想避免使用扩展方法,您可以随时使用:

var arr = new[] {"One", "Two", "Three", "Nine", "Four", "Seven", "Five"};

var result = arr.ToLookup(x => x.EndsWith("e"));

// result[true]  == One Three Nine Five
// result[false] == Two Four Seven
于 2013-08-12T22:54:35.630 回答
6

您应该通过扩展方法执行此操作(此方法假定您忽略分区项):

/// <summary>Splits an enumeration based on a predicate.</summary>
/// <remarks>
/// This method drops partitioning elements.
/// </remarks>
public static IEnumerable<IEnumerable<TSource>> Split<TSource>(
    this IEnumerable<TSource> source,
    Func<TSource, bool> partitionBy,
    bool removeEmptyEntries = false,
    int count = -1)
{
    int yielded = 0;
    var items = new List<TSource>();
    foreach (var item in source)
    {
        if (!partitionBy(item))
            items.Add(item);
        else if (!removeEmptyEntries || items.Count > 0)
        {
            yield return items.ToArray();
            items.Clear();

            if (count > 0 && ++yielded == count) yield break;
        }
    }

    if (items.Count > 0) yield return items.ToArray();
}
于 2012-07-11T17:44:34.097 回答
3
public static IEnumerable<IEnumerable<TSource>> Split<TSource>(
    this IEnumerable<TSource> source,
    Func<TSource, bool> predicate)
{
    List<TSource> group = new List<TSource>();
    foreach (TSource item in source)
    {
        if (predicate(item))
        {
            yield return group.AsEnumerable();
            group = new List<TSource>();
        }
        else
        {
            group.Add(item);
        }
    }
    yield return group.AsEnumerable();
}
于 2012-07-11T17:47:12.117 回答
1
public static IEnumerable<IEnumerable<TSource>> Partition<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
    yield return source.Where(predicate);
    yield return source.Where(x => !predicate(x));
}

例子:

var list = new List<int> { 1, 2, 3, 4, 5 };
var parts = list.Partition(x => x % 2 == 0);
var even = parts.ElementAt(0); // contains 2, 4
var odd = parts.ElementAt(1); // contains 1, 3, 5
于 2015-06-26T10:54:02.857 回答
1

我将使用提供的键选择器对源集合进行分区。这样,您还可以根据简单属性对复杂对象进行切片。

public static class LinqExtension
{
    public static IEnumerable<IEnumerable<TSource>> Slice<TSource, TKey>(
        this IEnumerable<TSource> source,
        Func<TSource, TKey> selector,
        Func<TKey, TKey, bool> partition)
    {
        if (source == null) throw new ArgumentNullException(nameof(source));
        if (selector == null) throw new ArgumentNullException(nameof(selector));
        if (partition == null) throw new ArgumentNullException(nameof(partition));

        var seed = new List<List<TSource>> { new List<TSource>() };

        return source.Aggregate(seed, (slices, current) => 
        {
            var slice = slices.Last();
            if (slice.Any())
            {
                var previous = slice.Last();
                if (partition(selector(previous), selector(current)))
                {
                    slice = new List<TSource>();
                    slices.Add(slice);
                }
            }
            slice.Add(current);
            return slices;

        }).Select(x => x.AsReadOnly());
    }
}

一个简单的例子:

// slice when the difference between two adjacent elements is bigger than 5
var input = new[] { 1, 2, 3, 10, 11, 20 };
var output = input.Slice(i => i, (x, y) => y - x > 5);
于 2019-08-20T18:50:27.437 回答