Clinton Pierce
Clinton Pierce

Reputation: 13209

Using Linq to select a range of members in a list

Given a list of elements like so:

int[] ia = new int[] { -4, 10, 11, 12, 13, -1, 9, 8, 7, 6, 5, 4, -2, 
                        6, 15, 32, -5, 6, 19, 22 };

Is there an easy way in Linq to do something along the lines of "Select the elements from the -1 up to the next negative number (or the list exhausts)"? A successful result for -1 would be (-1, 9, 8, 7, 6, 5, 4). Using -2 would give the result (-2, 6, 15, 32).

Not a homework problem. I'm just looking at an implementation using a bool, a for loop, and an if wondering if there's a cleaner way to do it.

Upvotes: 9

Views: 10854

Answers (4)

Rony
Rony

Reputation: 9511

var first = ia.Select((i, index) => new {i, index}).Where((i, index) => i.i == x).First();
var ind = first.index;
var second = ia.SkipWhile( (i, index) => index <= ind).TakeWhile(i => i > 0);
var ints = new[]{first.i}.Union(second);

Upvotes: 0

Anders Abel
Anders Abel

Reputation: 69290

Updated

This time I've tested the code... Using the two parameter form of TakeWhile we can force it to accept the first element (j == 0) even when the test for i is not satisfied.

ia.SkipWhile(i => i != -1).TakeWhile((i, j) => i >= 0 || j == 0)

TakeWhile(Func<int, int, bool>) requires a function/lambda that takes two parameters. The first being the value to be tested, the second being the index of the element.

Upvotes: 5

David Basarab
David Basarab

Reputation: 73351

Does it have to be Linq? You can use Extensions methods to get a cleaner solution.

int[] ia = new int[] { -4, 10, 11, 12, 13, -1, 9, 8, 7, 6, 5, 4, -2, 
                        6, 15, 32, -5, 6, 19, 22 };

// Call the Extension method
int[] results = ia.SelectRangeLoop(-2);

// Print Results
for (int i = 0; i < results.Length; i++)
{
    Console.Write(" {0} ", results[i]);
}

The method for SelectRangeLoop is below.

public static int[] SelectRangeLoop(this int[] value, int startNumber)
{
    List<int> results = new List<int>();

    bool inNegative = false;

    for (int i = 0; i < value.Length; i++)
    {
        if (value[i] == startNumber)
        {
            inNegative = true;

            results.Add(value[i]);

            continue;
        }

        if (inNegative && value[i] < 0)
        {
            break;
        }

        if (inNegative)
        {
            results.Add(value[i]);
        }
    }

    return results.ToArray();
}

Upvotes: 0

Erik van Brakel
Erik van Brakel

Reputation: 23840

Take a look at the TakeWhile Linq extension method. Takes items from the list as long as the condition is true, skips the rest.

Example:

int[] ia = new int[] { -4, 10, 11, 12, 13, -1, 9, 8, 7, 6, 5, 4, -2, 
                        6, 15, 32, -5, 6, 19, 22 };

var result = ia
             .SkipWhile(i => i != -1)
             .Skip(1)
             .TakeWhile(i => i >= 0);

Note the Skip(1) after the SkipWhile. SkipWhile skips everything up to, but not including the matching element. TakeWhile then Takes items, up to but not including the matching element. Because -1 is not greater than or equal to zero, you get an empty result.

Upvotes: 11

Related Questions