Splitting string into words length-based lists c#

У меня есть строка слов, разделенных пробелами. Как разбить строку на списки слов по длине слова?

пример

вход:

" aa aaa aaaa bb bbb bbbb cc ccc cccc cccc bbb bb aa "

выход:

List 1 = { aa, bb, cc}
List 2 = { aaa, bbb, ccc}
List 3 = { aaaa, bbbb, cccc}

5 ответов

Решение

Редактировать: Я рад, что мой оригинальный ответ помог ОП решить их проблему. Однако, немного подумав о проблеме, я адаптировал ее (и настоятельно рекомендую отказаться от своего прежнего решения, которое я оставил в конце статьи).

Простой подход

string input = " aa aaa aaaa bb bbb bbbb cc ccc cccc cccc bbb bb aa ";
var words = input.Trim().Split().Distinct();
var lookup = words.ToLookup(word => word.Length);

объяснение

Сначала мы обрезаем ввод, чтобы избежать пустых элементов из внешних пространств. Затем мы разбиваем строку на массив. Если между словами встречается несколько пробелов, вам нужно использовать StringSplitOptions как в ответе Марка.

После звонка Distinct чтобы включить каждое слово только один раз, теперь мы конвертируем words от IEnumerable<string> в Lookup<int, string> где длина слова представлена ​​ключом (int) и сами слова хранятся в значении (string),

Погоди, как это вообще возможно? Разве у нас нет нескольких слов для каждого ключа? Конечно, но это именно то, что Lookup класс существует для:

Lookup<TKey, TElement> представляет собой набор ключей, каждый из которых сопоставлен с одним или несколькими значениями. Lookup<TKey, TElement> напоминает Dictionary<TKey, TValue>, Разница в том, что словарь отображает ключи на отдельные значения, тогда как поиск отображает ключи на коллекции значений.

Вы можете создать экземпляр Lookup позвонив ToLookup на объекте, который реализует IEnumerable<T>,


Заметка
Нет открытого конструктора для создания нового экземпляра Lookup. Кроме того, объекты Lookup являются неизменяемыми, то есть вы не можете добавлять или удалять элементы или ключи из Lookup после того, как он был создан.

word => word.Length является лямбда- кодом KeySelector: он определяет, что мы хотим индексировать (или группировать, если хотите) Lookup по длине слова.

использование

Запишите все слова в консоль

(аналогично первоначально запрошенному выводу вопроса)

foreach (var grouping in lookup)
{
    Console.WriteLine("{0}: {1}", grouping.Key, string.Join(", ", grouping));
}

Выход

2: aa, bb, cc
3: aaa, bbb, ccc
4: aaaa, bbbb, cccc

Поместите все слова определенной длины в List

List<String> list3 = lookup[3].ToList();

Заказ по ключу

(обратите внимание, что они вернутся IOrderedEnumerable<T> поэтому доступ по ключу больше не возможен)

var orderedAscending = lookup.OrderBy(grouping => grouping.Key);
var orderedDescending = lookup.OrderByDescending(grouping => grouping.Key);

Оригинальный ответ - пожалуйста , не делайте этого (плохая производительность, помехи в коде):

string input = " aa aaa aaaa bb bbb bbbb cc ccc cccc cccc bbb bb aa ";
Dictionary<int, string[]> results = new Dictionary<int, string[]>();
var grouped = input.Trim().Split().Distinct().GroupBy(s => s.Length)
    .OrderBy(g => g.Key); // or: OrderByDescending(g => g.Key);
foreach (var grouping in grouped)
{
    results.Add(grouping.Key, grouping.ToArray());
}

Ты можешь использовать Where чтобы найти элементы, которые соответствуют предикату (в данном случае, с правильной длиной):

string[] words = input.Split();

List<string> twos = words.Where(s => s.Length == 2).ToList();
List<string> threes = words.Where(s => s.Length == 3).ToList();
List<string> fours = words.Where(s => s.Length == 4).ToList();

В качестве альтернативы вы можете использовать GroupBy найти все группы одновременно:

var groups = words.GroupBy(s => s.Length);

Вы также можете использовать ToLookup так что вы можете легко проиндексировать, чтобы найти все слова определенной длины:

var lookup = words.ToLookup(s => s.Length);
foreach (var word in lookup[3])
{
    Console.WriteLine(word);
}

Результат:

ааа
БББ
ссс

Посмотрите, как работает онлайн: ideone


В вашем обновлении похоже, что вы хотите удалить пустые строки и дублированные слова. Вы можете сделать первое с помощью StringSplitOptions.RemoveEmptyEntries и последний с помощью Distinct,

var words = input.Split((char[])null, StringSplitOptions.RemoveEmptyEntries)
                 .Distinct();
var lookup = words.ToLookup(s => s.Length);

Выход:

aa, bb, cc
aaa, bbb, ccc
aaaa, bbbb, cccc

Посмотрите, как работает онлайн: ideone

Во-первых, давайте объявим класс, который может содержать длину, а также список слов

public class WordList
{
    public int WordLength { get; set; }
    public List<string> Words { get; set; }
}

Теперь мы можем построить список списков слов с

string input = " aa aaa aaaa bb bbb bbbb cc ccc cccc ";
string[] words = input.Trim().Split();
List<WordList> list = words
    .GroupBy(w => w.Length)
    .OrderBy(group => group.Key)
    .Select(group => new WordList { 
        WordLength = group.Key, 
        Words = group.Distinct().OrderBy(s => s).ToList() 
    })
    .ToList();

Списки отсортированы по длине и по алфавиту соответственно.


Результат

например

list[2].WordLength ==> 4
list[2].Words[1] ==> "bbbb"

ОБНОВИТЬ

Если вы хотите, вы можете обработать результат немедленно, вместо того, чтобы поместить его в структуру данных

string input = " aa aaa aaaa bb bbb bbbb cc ccc cccc ";

var query = input
    .Trim()
    .Split()
    .GroupBy(w => w.Length)
    .OrderBy(group => group.Key);

// Process the result here
foreach (var group in query) {
    // group.Key ==> length of words
    foreach (string word in group.Distinct().OrderBy(w => w)) {
       ...
    }
}

Вы можете использовать Linq GroupBy

edit Теперь я применил Linq для генерации списка строк, который вы хотите вывести.

edit2 применил множественный ввод, один вывод, как в редактируемом вопросе. Это просто Отличный звонок в Linq

string input = " aa aaa aaaa bb bbb bbbb cc ccc cccc ";

var list = input.Split(' ');

var grouped = list.GroupBy(s => s.Length);

foreach (var elem in grouped)
{
    string header = "List " + elem.Key + ": ";
    // var line = elem.Aggregate((workingSentence, next) => next + ", " + workingSentence);

    // if you want single items, use this
    var line = elem.Distinct().Aggregate((workingSentence, next) => next + ", " + workingSentence);
    string full = header + " " + line;
    Console.WriteLine(full);
}


// output: please note the last blank in the input string! this generates the 0 list
List 0:  ,
List 2:  cc, bb, aa
List 3:  ccc, bbb, aaa
List 4:  cccc, bbbb, aaaa

Немного длинное решение, но результат получается в словаре

class Program
    {
        public static void Main()
        {
            Print();
            Console.ReadKey();
        }

        private static void Print()
        {
            GetListOfWordsByLength();

            foreach (var list in WordSortedDictionary)
            {
                list.Value.ForEach(i => { Console.Write(i + ","); });
                Console.WriteLine();
            }
        }

        private static void GetListOfWordsByLength()
        {
            string input = " aa aaa aaaa bb bbb bbbb cc ccc cccc ";

            string[] inputSplitted = input.Split(' ');

            inputSplitted.ToList().ForEach(AddToList);
        }

        static readonly SortedDictionary<int, List<string>> WordSortedDictionary = new SortedDictionary<int, List<string>>();

        private static void AddToList(string s)
        {
            if (s.Length > 0)
            {
                if (WordSortedDictionary.ContainsKey(s.Length))
                {
                    List<string> list = WordSortedDictionary[s.Length];
                    list.Add(s);
                }
                else
                {
                    WordSortedDictionary.Add(s.Length, new List<string> {s});
                }
            }
        }
    }
Другие вопросы по тегам