Failed Scientist
Failed Scientist

Reputation: 2027

Applying "where" clause (LINQ) on even rows only [C#]

I have a string having two different types of data in alternating rows (i.e. two rows make one record). I want to select only those records where length of 2nd (i.e. even row) is less than 1000.

I have tried this but it results in selecting only the eventh row and discards the odd row:

var lessthan1000Length = recordsFile.Where((src, index) => src.Length<1000 && index%2 != 0);

Sample data from recordsFile

2012-12-04 | 10:45 AM | Lahore
Added H2SO4 in the solution. Kept it in the lab temperature for 10 minutes    
2012-12-04 | 10:55 AM | Lahore
Observed the pH of the solution.     
2012-12-04 | 11:20 AM | Lahore
Neutralized the solution to maintain the pH in 6-8 range

Thanks for your guidance.

P.S: Kindly note that the results are required in the form of List<string> as we have to make a new dataset from it.

Upvotes: 1

Views: 664

Answers (4)

Enigmativity
Enigmativity

Reputation: 117154

If you use Microsoft's Reactive Framework team's "Interactive Extensions" you get a nice extension method that can help you.

var query = 
    from pair in lines.Buffer(2)
    where pair[1].Length < 1000
    select pair;

var results = query.ToList();

From your sample data I get this:

results

Just NuGet "Ix-Main" to get the extension methods - there are a lot more there than just .Buffer and many of them are super useful.

Upvotes: 1

Jeppe Stig Nielsen
Jeppe Stig Nielsen

Reputation: 62002

Alexander's answer seems to work fine.

Alternatively, you can create a method to turn a sequence (with an even number of terms) into a sequence of pairs. I guess something like:

static IEnumerable<Tuple<T, T>> PairUp<T>(this IEnumerable<T> src)
{
  using (var e = src.GetEnumerator)
  {
    while (e.MoveNext())
    {
      var first = e.Current;

      if (!e.MoveNext())
        throw new InvalidOperationException("Count of source must be even"); // OR: yield break; OR yield return Tuple.Create(first, default(T)); yield break;
      var second = e.Current;

      yield return Tuple.Create(first, second);
    }
  }
}

With that you could do recordsFile.PairUp().Where(t => t.Item2.Length < 1000) or similar.

Edit: Since you want the two "parts" concatenated as strings, that would be recordsFile.PairUp().Where(t => t.Item2.Length < 1000).Select(t => t.Item1 + t.Item2).

Upvotes: 1

Amy B
Amy B

Reputation: 110201

List<string> result = recordFile
  .Select( (str, index) => new {str, index})
  .GroupBy(x => x.index / 2, x => x.str)
  .Where(g => g.Last().Length < 1000)
  .Select(g => g.First() + g.Last())
  .ToList();

Upvotes: 2

Alexander Petrov
Alexander Petrov

Reputation: 14251

var odds = recordsFile.Where((str, index) => index % 2 == 0);
var evens = recordsFile.Where((str, index) => index % 2 == 1);

var records = odds.Zip(evens, (odd, even) => new { odd, even })
    .Where(pair => pair.even.Length < 1000);

foreach (var record in records)
    Console.WriteLine(record);

Upvotes: 5

Related Questions