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