将IEnume拆分为三个部分:""," item"," under"有效率

时间:2014-09-03 17:07:07

标签: c# linq ienumerable

IEnumerable<int> list = new[] { 1, 2, 7, 3, 11, 5 };
int item = (from x in list where (x == list.Max()) select x).First();
IEnumerable<int> above = from x in list where list.ToList().IndexOf(item) > list.ToList().IndexOf(x) select x;
IEnumerable<int> below = from x in list where list.ToList().IndexOf(item) < list.ToList().IndexOf(x) select x;

我想在IEnumerable中找到一个元素,并将IEnumerable拆分为不再包含我找到的元素的IEnumerable。上面的代码说明了我想要实现的结果,但是我必须将IEnumerable转换为List。

我觉得必须有一种方法可以使用LinQ和IEnumerable来做到这一点。 怎么办呢?

4 个答案:

答案 0 :(得分:14)

所以我们这里有几个子问题。第一个问题是返回一个集合中的项目,该集合具有该项目的某些投影的最高值Max仅比较项目本身,或者,如果给定投影,则返回该投影的结果。

public static TSource MaxBy<TSource, TKey>(this IEnumerable<TSource> source
    , Func<TSource, TKey> selector
    , IComparer<TKey> comparer = null)
{
    if (comparer == null)
    {
        comparer = Comparer<TKey>.Default;
    }
    using (IEnumerator<TSource> iterator = source.GetEnumerator())
    {
        if (!iterator.MoveNext())
        {
            throw new ArgumentException("Source was empty");
        }

        TSource maxItem = iterator.Current;
        TKey maxValue = selector(maxItem);

        while (iterator.MoveNext())
        {
            TKey nextValue = selector(iterator.Current);
            if (comparer.Compare(nextValue, maxValue) > 0)
            {
                maxValue = nextValue;
                maxItem = iterator.Current;
            }
        }
        return maxItem;
    }
}

这使我们能够更有效地获得具有最大值的项目的索引:

var splitPoint = list.Select((index, number) => new { index, number })
    .MaxBy(pair => pair.number)
    .index;

在拆分集合旁边,您只需使用skip / take:

var firstHalf = list.Take(index);
var secondHalf = list.Skip(index + 1);

您在此处解决的代码存在许多不同的问题。

您为查询中的每个项计算Max以获取item,而不是计算一次并使用该计算值。

然后,对于列表中的每个项目,然后将所有项目复制到新列表,两次,搜索该列表以尝试查找最大项目的位置,并且然后尝试找到当前项目的位置。然后,你做了两次。这意味着您将每个项目的整个数组复制到列表中四次,在集合中每个项目搜索最多项目的位置四次,并线性搜索列表以查找当前项目的索引(某些内容)你可以在几乎没有时间计算每个项目只计算两次。随着物品数量的增加,这将会很难......。

此处的代码在集合的单个过程中查找最大项目的索引,然后创建表示每个半部分的序列,除了简单地遍历这些项目之外,每个部分几乎没有任何开销。

答案 1 :(得分:4)

尝试使用可以使用索引的扩展方法。请看下面的评论示例。

// define the list
IEnumerable<int> list = new[] { 1, 2, 7, 3, 11, 5 };

// define some value (max in your sample)
int value = list.Max();

// get the index of the value you want
int indexValue = list.ToList().IndexOf(value);

// find collections
IEnumerable<int> above = list.Where((value, index) => index < indexValue);
IEnumerable<int> below = list.Where((value, index) => index > indexValue);

答案 2 :(得分:3)

首先,您需要找到最大元素的(第一个)索引。作为Servy答案的变体,可以使用SelectAggregate来完成。然后枚举该索引之前和之后的元素:

        var indexOfMax = list
            .Select((value, index) => new KeyValuePair<int, int>(index, value))
            .Aggregate(new KeyValuePair<int, int>(-1, -1), (min, cur) => 
                {
                    if (min.Key == -1 || cur.Value > min.Value)
                        return cur;
                    return min;
                }).Key;

        var beginning = list.Take(indexOfMax);
        var end = list.Skip(indexOfMax + 1);

答案 3 :(得分:0)

首先,您需要保证您的IEnumerable<T>对象包含某些特定顺序的项目。这可以通过使用IOrderedEnumerable<T>代替普通无序IEnumerable<T>,或者使用可以通过索引引用元素的IList<T>来完成。

当你想到这一点时,拆分变得非常简单:按索引或顺序遍历元素并将它们添加到IList<T> above,直到找到你的元素。跳过该元素,然后继续迭代元素,同时将它们添加到IList<T> below