Получить строку значений и их количество с помощью LINQ

#string #vb.net #linq

#строка #vb.net #linq

Вопрос:

У меня есть список учетных записей, разделенных pipe, мне нужно вернуть список с указанием того, сколько раз эта учетная запись появляется в списке. Новый список должен иметь то же количество индексов, что и исходный список.

Пример:

INPUT: 111|111|222|333|333|111|333|444|555|555|111

OUTPUT: 4|4|1|3|3|4|3|1|2|2|4

До сих пор мне удавалось получить только сумму учетных записей, но она группирует их вместе:

 string.Join("|",  str.Split("|").GroupBy(Function(x) x).[Select](Function(y) y.Count()).ToList())
 

INPUT: 111|111|222|333|333|111|333|444|555|555|111

OUTPUT: 4|1|3|1|2

Спасибо за любую помощь

РЕДАКТИРОВАТЬ 1: Извинения, похоже, я перепутал пример ВВОДА / вывода, не соответствующий требованиям. Я также забыл упомянуть еще одно требование — последовательность выходных элементов должна совпадать с исходной (см. Обновленный пример ВВОДА / ВЫВОДА).

Ответ №1:

Вы можете разделить входную строку, используя известный разделитель, сгруппировать по разделенным значениям, Join() вернуть каждую часть разделителем, используя строки для каждой группировки в качестве итератора конечной Select() части, где строковые значения группировки фактически не учитываются, в то время Group.Count() как:

 Dim input = "111|111|222|333|333|333|444|555|555"
Dim output = String.Join("|"c, acc.Split("|"c).GroupBy(Function(s) s).
    Select(Function(grp) String.Join("|", grp.Select(Function(nan) grp.Count()))))
 

output находится 2|2|1|3|3|3|1|2|2 здесь.

Есть Aggregate() вариант, на случай, если он более интересен: вы можете объединить полученный результат Grouping.Count() с помощью StringBuilder и Concat() final IEnumerable<StringBuilder> :

 Dim output = String.Concat(input.Split("|"c).GroupBy(Function(s) s).Select(Function(g) 
    g.Aggregate(New StringBuilder(), Function(sb, s) sb.AppendFormat("{0}|", g.Count()))))
 

Поскольку на самом деле это сегментированный — без последовательного ввода, простой способ — превратить группировку в словарь и использовать последовательные элементы разделенной входной строки в качестве ключа:

 Dim splitInput = input.Split("|"c)
Dim groups = splitInput.GroupBy(Function(s) s).ToDictionary(Function(g) g.Key, Function(g) g.Count)

' Aggregate using a StringBuilder
Dim output = splitInput.Aggregate(
    New StringBuilder, Function(sb, s) sb.AppendFormat("{0}|", groups(s))).ToString()

' Or Join values as strings
Dim output = String.Join("|"c, splitInput.Select(Function(s) groups(s)))
 

Комментарии:

1. Спасибо вам за ваш пример. Я забыл упомянуть еще одно требование: последовательность выходных элементов должна совпадать с входными элементами. Я обновил свой вопрос. Для ввода 111|111|222|333|333|111|333|444|555|555|111 оба ваших примера кода выдают 4|4|4|4|1|3|3|3|1|2|2| , но должны выдавать 4|4|1|3|3|4|3|1|2|2|4 . Он не должен группировать итоговые значения, как это было сделано 4 для 111

2. Посмотрите редактирование, может быть, оно достаточно хорошо для того, что вам нужно.

3. Новый код отлично работает как для агрегирования, так и для объединения. Я работал над аналогичным кодом, который у вас есть, вплоть до получения словаря с итогами, но затем у меня был цикл For Each, идущий против каждого элемента и ищущий ключ в словаре и добавляющий итог. Я надеялся получить версию этого кода на LINQ, но мои ограниченные знания LINQ заставили меня бороться. Спасибо за вашу помощь

Ответ №2:

Создайте a Dictionary для сопоставления учетных записей с их количеством:

 Dim accountCountMap = str.Split("|").GroupBy(Function(s) s) _
                                    .Select(Function(sg) (sg.Key, sg.Count())) _
                                    .ToDictionary(Function(kc) kc.Key, Function(kc) kc.Count)
 

Затем используйте Dictionary для сопоставления учетных записей с их учетными записями:

 Dim ans = String.Join("|", str.Split("|").Select(Function(s) accountCountMap(s)))
 

Комментарии:

1. Я получаю эту ошибку при использовании вашего кода Error BC37289 Tuple element name 'Key' is inferred. Please use language version 15.3 or greater to access an element by its inferred name.