#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.