Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Divide list in sublists by elements

Tags:

c#

linq

I have a list of strings:

{"foo", "str1", "str2", ..., "bar", ..., "baz", ...}

I need to get sublists of strings between "foo", "bar" and "baz".

Is it possible to do this with linq?

EDIT
I need a method without looking trough the list twice.

like image 688
dan.lavr Avatar asked Dec 26 '22 02:12

dan.lavr


1 Answers

You can do this to fine all elements between any two other elements:

var strings = new[] { "foo", "str1", "str2", ... "bar", ... "baz" };
var between = strings.SkipWhile(s => s != "foo").Skip(1)
                     .TakeWhile(s => s != "bar"); // "str1", "str2", ...

If you want to get everything between "foo" and "baz", except "bar", use this (assuming the order "foo", "bar", "baz"):

var strings = new[] { "foo", "str1", "str2", ... "bar", ... "baz" };
var between = strings.SkipWhile(s => s != "foo").Skip(1)
                     .TakeWhile(s => s != "baz")
                     .Where(s => s != "bar"); // "str1", "str2", ...

Or if your comfortable with using Linq queries with side effects, you can do this to partition your input list by certain 'stop' words:

 var stops = new[] { "foo", "bar", "baz" };
 var strings = new[] { "foo", "str1", "str2", "bar", "str3", "baz" };
 var p = -1;
 var partitions = 
     from s in strings
     let i = Array.IndexOf(stops, s) 
     group s by p = i == -1 ? p : i into g
     where g.Key == 0 || g.Key == 1
     select g.Skip(1); // { "str1", "str2" }, { "str3" }

Or slightly more efficient (since it stops processing after the third stop word):

 var partitions = 
     (from s in strings
      let i = Array.IndexOf(stops, s) 
      group s by p = i == -1 ? p : i)
     .SkipWhile(g => g.Key < 0)
     .Take(2)
     .Select(g => g.Skip(1)); // { "str1", "str2" }, { "str3" }

Now, this method is a little bit rough around the edges, and it's somewhat fiddly when it comes to items before "foo" or after "baz", but if since you're only looking for items between "foo" and "baz", it should work for you. It has the added benefit that the order of the stop words does not affect the results.

like image 172
p.s.w.g Avatar answered Jan 12 '23 05:01

p.s.w.g