2013-06-11 2 views
4

У меня есть список строк:список Divide в подсписках элементов

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

Мне нужно получить подсписки строк между "foo", "bar" и "baz".

Можно ли это сделать с помощью linq?

EDIT
Мне нужен метод, не глядя корыта список дважды.

+0

Я бы сказал, да :) Вы уже что-то пробовали? Если да, поделитесь им с нами. – wonko79

+0

Ну, я потратил два дня на поиски в Интернете и пытался использовать разные методы, но не нашел метода, который делает это, не просматривая дважды список. – dikyyn

+0

См. Мой ответ для повторения только один раз. Выглядит не так хорошо, как другие решения, но и делает трюк. – wonko79

ответ

6

Вы можете сделать это точно все элементы между любыми двумя другими элементами:

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

Если вы хотите, чтобы получить все, что между «Foo» и «Баз», кроме «бар», используйте этот (предполагается, что порядок «Foo», «бар», «Баз»):

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", ... 

Или, если ваш удобно с помощью Linq запросов с побочными эффектами, вы можете сделать это, чтобы разбить список ввода с определенным «стоп» слова:

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" } 

Или немного более эффективным (так как она прекращает обработку после третьего слова остановки):

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" } 

Теперь этот метод немного грубо вокруг краев, и это несколько неудобным, когда речь идет о пункты перед «foo» или после «baz», но если вы только ищите предметы между «foo» и «baz», он должен работать на вас. Он имеет дополнительное преимущество, что порядок слов остановки не влияет на результаты.

+0

Да, но мне также нужен подсписчик между '" bar "и' "baz". С помощью моего метода мне нужно дважды просмотреть список. Мне интересно, можно ли это сделать в одной строке. – dikyyn

+0

@ dikyyn, если вы знаете порядок «foo», «bar», «baz», вы можете просто сделать это за один проход и исключить «бар». См. Мой обновленный ответ. –

+0

Я знаю, что это кажется невозможным, но мне нужно 2 отдельных подсписок – dikyyn

4
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); 
0

Если вы хотите перебрать только один раз через обширный список данных, вы можете сделать это:

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); 
    }); 

использует по крайней мере немного, но LINQ делает трюк переборе только один раз через обширный список данных.

Смежные вопросы