Reputation: 71
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.
Upvotes: 4
Views: 461
Reputation: 149030
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.
Upvotes: 6
Reputation:
If you want to iterate only once through your extensive data list you can do this:
List<string> longDataList = new List<string> { "foo", "str1", "str2", "str1", "str2", "str1", "str2", "bar", "str1", "str2", "str1", "str2", "str1", "str2", "baz", "str1", "str2", "str1", "str2", "str1", "str2" };
List<string> splitters = new List<string> { "foo", "bar", "baz" };
Dictionary<string, List<string>> resultDict = new Dictionary<string, List<string>>();
List<string> currentList = null;
longDataList.ForEach(s =>
{
if (splitters.Contains(s))
{
if (resultDict.ContainsKey(s))
currentList = resultDict[s];
else
{
currentList = new List<string>();
resultDict.Add(s, currentList);
}
}
else
currentList.Add(s);
});
Uses at least a little linq but does the trick iterating only once through your extensive data list.
Upvotes: 0
Reputation: 17603
var idxFoo = list.IndexOf("foo");
var idxBar = list.IndexOf("bar");
var idxBaz = list.IndexOf("baz");
var subList1 = list.Skip(idxFoo).Take(idxBar - idxFoo);
var subList2 = list.Skip(idxBar).Take(idxBaz - idxBar);
Upvotes: 4