2016-02-12 2 views
5

Я относительно новичок в LINQ и в настоящее время работает над запросом, который объединяет группировку и сортировку. Я собираюсь начать с примера здесь. В принципе у меня есть произвольная последовательность чисел, представленные в виде строк:Запрос LINQ, который объединяет группировку и сортировку

List<string> sNumbers = new List<string> {"34521", "38450", "138477", "38451", "28384", "13841", "12345"} 

Мне нужно найти все sNumbers в этом списке, которые содержат шаблон поиска (скажем, «384») затем возвращает отфильтрованную последовательность такой, что sNumbers что начните с шаблона поиска («384»), а затем остальные sNumbers, содержащие где-нибудь шаблон поиска. Так что это будет так (пожалуйста, обратите внимание на алфавитный СНП в группах):

{"38450", "38451", "13841", "28384", "138477"} 

Вот как я начал:

outputlist = (from n in sNumbers 
       where n.Contains(searchPattern 
       select n).ToList(); 

Так что теперь мы все числа, которые содержат поиск шаблон. И здесь я застрял. Я знаю, что в этот момент мне нужно «сгруппировать» результаты в две последовательности. Один, который начинается с шаблона поиска и других, которые этого не делают. Затем примените вторичную сортировку в каждой группе по алфавиту. Как написать запрос, который объединяет все это?

+0

к сожалению, после прочтения ответов, я понимаю, что я сделал ошибку выше правильный ответ будет. следующим образом: (сортировка начинается с, а затем в алфавитном порядке (не численно) {"38450", "38451", "13841", "138477", "28384"} –

ответ

2

Это кажется довольно прямо вперед, если я не понял что-то:

List<string> outputlist = 
    sNumbers 
     .Where(n => n.Contains("384")) 
     .OrderBy(n => int.Parse(n)) 
     .OrderByDescending(n => n.StartsWith("384")) 
     .ToList(); 

Я получаю это:

outputlist

+0

Единственное, что вторичный сорт был алфавитным, а не числовым. Я смог достичь этого, следуя приведенному ниже коду: Список результат = sNumbers . Где (n => n.Содержит (searchpattern)) .OrderBy (s =>! S.StartsWith (searchpattern)) .ThenBy (s => s) .ToList(); Спасибо за помощь –

2

Здесь оптимизированная версия который нужен только один LINQ заявление:

string match = "384"; 
List<string> sNumbers = new List<string> {"34521", "38450", "138477", "38451", "28384", "13841", "12345"}; 

// That's all it is 
var result = 
    (from x in sNumbers 
    group x by new { Start = x.StartsWith(match), Contain = x.Contains(match)} 
    into g 
    where g.Key.Start || g.Key.Contain 
    orderby !g.Key.Start 
    select g.OrderBy(Convert.ToInt32)).SelectMany(x => x); 

result.ToList().ForEach(x => Console.Write(x + " ")); 

шаги:

1.) Группа в группе г на основе StartsWith и содержит

2.) Просто выберите группы, которые содержат совпадение

3.) Сортировать по обратной ключа StartsWith (Так что StartsWith = истина предшествует StartsWith = ложь)

4.) Выберите отсортированный список элементов обеих групп

5.) Выполните flatMap (SelectMany) над как списки, чтобы получить один итоговый список результатов


Здесь в неоптимизированная версии:

string match = "384"; 
List<string> sNumbers = new List<string> {"34521", "38450", "138477", "38451", "28384", "13841", "12345"}; 
var matching = from x in sNumbers 
       where x.StartsWith(match) 
       orderby Convert.ToInt32(x) 
       select x; 
var nonMatching = from x in sNumbers 
        where !x.StartsWith(match) && x.Contains(match) 
        orderby Convert.ToInt32(x) 
        select x; 
var result = matching.Concat(nonMatching); 

result.ToList().ForEach(x => Console.Write(x + " ")); 
+1

Thx для вашей помощи. Я смог добиться этого с помощью более простого решения, используя синтаксис метода расширения. Fike –

0

Linq имеет метод OrderBy, который позволяет дать пользовательский класс дес как вещи должны быть отсортированы. Посмотрите здесь: https://msdn.microsoft.com/en-us/library/bb549422(v=vs.100).aspx

Затем вы можете написать свой класс IComparer, который принимает значение в конструкторе, а затем метод сравнения, который предпочитает значения, начинающиеся с этого значения.

Что-то вроде этого, может быть:

public class CompareStringsWithPreference : IComparer<string> { 
    private _valueToPrefer; 

    public CompareStringsWithPreference(string valueToPrefer) { 
     _valueToPrefer = valueToPrefer; 
    } 

    public int Compare(string s1, string s2) { 
     if ((s1.StartsWith(_valueToPrefer) && s2.StartsWith(_valueToPrefer)) || 
      (!s1.StartsWith(_valueToPrefer) && !s2.StartsWith(_valueToPrefer))) 
       return string.Compare(s1, s2, true); 

     if (s1.StartsWith(_valueToPrefer)) return -1; 
     if (s2.StartsWith(_valueToPrefer)) return 1; 
    } 
} 

Затем использовать его как это:

outputlist = (from n in sNumbers 
      where n.Contains(searchPattern) 
      select n).OrderBy(n, new CompareStringsWithPreference(searchPattern))ToList(); 
+0

Вы должны удалить n.Contains (searchPattern). Если вы этого не сделаете, вы будете работать только с подмножеством – CodeNotFound

+0

Из того, что я понял, это то, что он хочет: «Мне нужно найти все sNumbers в этом списке, которые содержат шаблон поиска (скажем,« 384 »), а затем вернуть отфильтрованную последовательность, такую ​​как что sNumbers, которые начинаются с шаблона поиска («384»), сначала сортируются ». –

0

Вы можете создать список со строками, начиная с searchPattern переменной и другой, содержащий searchPattern, но не начиная с (в избегать повторяющихся элементов в обоих списках):

string searchPattern = "384"; 
List<string> sNumbers = new List<string> { "34521", "38450", "138477", "38451", "28384", "13841", "12345" }; 

var list1 = sNumbers.Where(s => s.StartsWith(searchPattern)).OrderBy(s => s).ToList(); 
var list2 = sNumbers.Where(s => !s.StartsWith(searchPattern) && s.Contains(searchPattern)).OrderBy(s => s).ToList(); 

var outputList = new List<string>(); 

outputList.AddRange(list1); 
outputList.AddRange(list2); 
2
var result = sNumbers 
         .Where(e => e.StartsWith("384")) 
         .OrderBy(e => Int32.Parse(e)) 
       .Union(sNumbers 
         .Where(e => e.Contains("384")) 
         .OrderBy(e => Int32.Parse(e))); 
+0

Это не будет сортировать численно –

+1

@MarkusWeninger, да, вы правы, исправлены, спасибо –

+0

Ваш ответ имеет смысл, но мы сортируем по алфавиту, а не по цифре –

3

Я думаю, что вам не нужны никакие группировки, ни списка расщепления для получения нужного результата, поэтому вместо ответа о объединения и группировки Я вывешу то, что я хотел бы сделать, чтобы получить желаемый результат:

sNumbers.Where(x=>x.Contains(pattern)) 
    .OrderByDescending(x => x.StartsWith(pattern)) // first criteria 
    .ThenBy(x=>Convert.ToInt32(x)) //this do the trick instead of GroupBy 
    .ToList(); 
0

Извините, ребята, прочитав ответы, я понимаю, что я ошибся в своем вопросе. Правильный ответ был бы следующим: (сортировать по «начинается с» сначала, а затем в алфавитном порядке (не численно)

// вывод: {"38450", "38451", "13841", "138477", "28384 «}

мне удалось добиться того, что с помощью следующего запроса:

string searchPattern = "384"; 
List<string> result = 
          sNumbers 
           .Where(n => n.Contains(searchpattern)) 
           .OrderBy(s => !s.StartsWith(searchpattern)) 
           .ThenBy(s => s) 
           .ToList(); 

Благодаря