Peter17
Peter17

Reputation: 3102

Linq Query Question

There is standard way to count number of elements which fit some condition:

collection.Where(d=> d==something).Count();

I need the following functionality (pseudo-cod):

collection.Where(d=> (d==something) && (d.Next == somethingElse)).Count();

EDIT: d.Next - is the next element after d in the collection.

How this can be achieved?

Upvotes: 3

Views: 147

Answers (5)

Femaref
Femaref

Reputation: 61497

You can write a new operator (assuming Linq-to-objects) that checks this using the enumerator explicitly.

Code:

public static partial class Enumerable
{
    public static IEnumerable<TSource> WhereNext<TSource> (this IEnumerable<TSource> source, Func<TSource, bool> predicate, TSource next)
    {
        if (source == null)
            throw new ArgumentNullException("source");
        if (predicate == null)
            throw new ArgumentNullException("predicate");

        return WhereNextImpl(source, predicate, next);
    }

    private static IEnumerable<TSource> WhereNextImpl<TSource>(IEnumerable<TSource> source, Func<TSource, bool> predicate, TSource next)
    {
        using (var enumerator = source.GetEnumerator())
        {
            TSource current;
            TSource nextElement;

            if (!enumerator.MoveNext())
                yield break;
            while (true)
            {
                current = enumerator.Current;
                if (!enumerator.MoveNext())
                    yield break;
                nextElement = enumerator.Current;

                if (predicate(current) && EqualityComparer<TSource>.Default.Equals(next, nextElement))
                    yield return current;
            }
        }
    }
}

Caveat: Currently uses EqualityComparer<TSource>.Default for comparision. Another implementation with a custom comparer should be done.

Upvotes: 1

dtb
dtb

Reputation: 217411

You can use the Aggregate Method to create a custom sum:

var result = collection.Aggregate(
    Tuple.Create(false, 0),
    (s, x) => Tuple.Create(x == something,
                           s.Item1 + (s.Item0 && (x == somethingElse) ? 1 : 0)),
    s => s.Item1);

It works like this:

Item            Accumulator
--------------- ---------------
                (false, 0)
foo             (false, 0)
something       (true, 0)
bar             (false, 0)
something       (true, 0)
somethingElse   (false, 1)
somethingElse   (false, 1)
baz             (false, 1)
                ---------------
                Result: 1

Upvotes: 1

Oleks
Oleks

Reputation: 32343

var result = collection.Count(d => 
     d == something && 
     d.Next == somethingElse
);

EDIT: in the case if d.Next a property of d or the next element in the sequence:

var result = collection.Zip(
    collection.Skip(1),
    (first, second) => first == something && second == somethingElse
).Count(i => i);

Upvotes: 3

Ani
Ani

Reputation: 113472

Assuming that what you have is a predicate that involves consecutive elements in the source-sequence, you can do:

int numMatches = collection.Zip(collection.Skip(1), (prev, next) =>
                                prev == something && next == somethingElse)
                           .Count(match => match)

This overlays the sequence on a one-deferred version of the sequence before applying the filter.

Upvotes: 5

Bala R
Bala R

Reputation: 109027

if collection were List of stirng, you could try something like

var selectC = from c in collection
let nextC = collection.IndexOf(c) == collection.Count - 1 ? null : collection[collection.IndexOf(c) + 1]
where string.IsNullOrEmpty(c) && string.IsNullOrEmpty(nextC)
select c;

queries involving let are tricky to translate into method chain but I got this from Resharper's automatic conversion

var selectC =
    collection.Select(
        c =>
        new {c, nextC = collection.IndexOf(c) == collection.Count - 1 ? null : collection[collection.IndexOf(c) + 1]}).
        Where(@t => string.IsNullOrEmpty(@t.c) && string.IsNullOrEmpty(@t.nextC)).Select(@t => @t.c);

Upvotes: 1

Related Questions