#vb.net #linq
#vb.net #linq
Вопрос:
Все, у меня есть таблица данных, один из столбцов (строковый тип) имеет значение, подобное этому
хотите добавить еще один столбец, и он должен быть таким
где столбец с правой стороны указывает, сколько раз значение с левой стороны возникло до сих пор. Я хочу, чтобы это было сделано через LINQ. У меня уже есть какой-то метод, который очень эффективен.
Мой алгоритм: предположим, что имя столбца, с которым мы имеем дело, равно col1, а новый столбец, который мы хотим добавить, равен col2
for every unique value in the data table in col1
collect the indices on which this value appear
in data table on column2 for each row of these indices put a value as 1 ,2,3
Комментарии:
1.Одно слово
GroupBy
docs.microsoft.com/en-us/dotnet/visual-basic/language-reference /…2. привет, я новичок в этом, не могли бы вы немного рассказать о том, как это сделать? моя группа по результатам будет похожа на abc, будет равна 2, а bcd будет равен 3 и т. Д., Максимум, я могу составить из этого словарь. но то, что мне нужно, немного отличается. каждая строка должна иметь номер вхождения . скажем, bcd будет иметь 3 числа 1,2 и 3.
3. Пожалуйста, не размещайте данные в виде изображений. Пожалуйста, опубликуйте его как действительный VB.NET код. Тогда мы можем легко опубликовать ответ.
4. Я не специалист по VB. Но в C # это будет
items.GroupBy(i => i.Name).Select(g => new { Name = g.Key, Count = g.Count()});
5. результатом этого может быть dict, имеющий abc -2, bcd — 3 и т. Д., Моя точка включает первое вхождение, Каждая строка должна быть связана со списком строк .. этот список для abc равен 1,2 для bcd 1,2,3
Ответ №1:
О, дорогой, я почти закончил свой ответ, когда заметил, что вы хотите его в VB, а не в C #!
Ну что ж, может быть, вы все еще понимаете методы, которые я использую, или какой-нибудь другой читатель может это использовать.
Поскольку в некоторых операторах LINQ вы можете потерять исходный порядок, вы могли бы сначала добавить индекс к исходной последовательности имен. Затем вы создаете группы с одинаковыми именами, упорядочиваете элементы в группе по индексу, затем добавляете «AppearanceCount», разгруппируйте все элементы и, наконец, упорядочите по исходному индексу.
Не уверен, что это эффективно:
IEnumerable<string> names = ...
var result = names.Select( (name, i) => new
{
Index = i,
Name = name,
})
Итак, {ABC DEF GH, …}I => { {0, ABC}, {1, DEF}, {2, GHI}, {3, …
// GroupBy same Name:
.GroupBy(indexedName => indexedName.Name,
// parameter resultSelector: take every Name and all IndexNames with this name
// to make one new:
(name, indexedNamesInThisGroup) => new
{
Name = name,
// from every IndexedName in this Group: OrderBy index, and add a Count:
Indexes = indexNamesInThisGroup
.OrderBy(indexedName => indexName.Index)
.Select( (indexedName, i) => new
{
Index = indexedName.Index,
Count = i 1,
},
});
Вы создали группы и упорядочили элементы, затем добавили количество. Поскольку вы начинаете свой первый подсчет с 1, мне нужно добавить 1;
Например:
{0, ABC}, {1, DEF}, {2, ABC}, {3, XYZ}, {4, ABC}, {5, XYZ}, {6, ABC}, ... =>
Group ABC has elements {0, 1}, {2, 2}, {4, 3} {6, 4}
Group DEF has elements {1, 1}
Group XYZ has elements {3, 1} {5, 2}
Таким образом, из каждой группы ключ имеет имя всех элементов в группе. У каждого члена группы есть исходный номер индекса и счетчик: «этот исходный номер индекса был первым / вторым / третьим появлением ABC»
Теперь все, что вам нужно сделать, это использовать SelectMany для разгруппировки, упорядочения по исходной последовательности и удаления исходного индекса:
.SelectMany(group => group, (key, groupElement) => new
{
Name = key,
Index = groupElement.Index,
Count = groupElement.Count,
})
.OrderBy(ungroupedElement => ungroupedElement.Index)
.Select(ungroupedElement => new
{
Name = ungroupedElement.Name,
Count = ungroupedElement.Count,
});
Это один ужасный оператор LINQ. Я не знаю, сколько раз это будет перечислять вашу исходную последовательность.
Я думаю, что метод расширения, подобный следующему, будет более эффективным.
public static IEnumerable<CountedName> ToCountedNames(this IEnumerable<string> names)
{
Dictionary<string, int> countedNames = new Dictionary<string, int>();
foreach (string name in names)
{
// did we see this name before?
int nameCount;
if (countedNames.TryGetValue(name, out nameCount)
{
// Yes we saw it before, add 1
nameCount = 1;
countedNames[name] = nameCount;
}
else
{
// no we didn't see it before, add this name with a Count 1:
nameCount = 1;
countedNames.Add(name, nameCount);
}
yield return new CountedName
{
Name = name,
Count = nameCount,
};
}
}
Использование:
IEnumerable<string> names = ...
IEnumerable<CountedName> countedNames = names.ToCountedNames();
Ответ №2:
Предложение, данное Святославом в комментариях, делает не совсем то, что вы хотите, поскольку оно использует GroupBy для подсчета общего количества, в то время как вам нужен текущий подсчет. Для достижения этого вам необходимо иметь какой-то метод упорядочивания элементов в ваших данных. В качестве иллюстрации я показываю некоторый код ниже (используя простое консольное приложение VB):
Сначала вам нужен класс, подобный этому:
Public Class Item
Public Sub New(pid As Int32, pname As String)
ID = pid
Name = pname
End Sub
Public Property Name As String
Public Property ID As Int32
End Class
Теперь вы можете использовать этот класс в основном подразделе таким образом:
Sub Main()
Dim items As New List(Of Item)({New Item(1, "abc"), New Item(2, "bcd"), New Item(3, "abc"), New Item(4, "pen"), New Item(5, "efh"), New Item(6, "vgn"), New Item(7, "bcd"), New Item(8, "mno"), New Item(9, "tap"), New Item(10, "pen"), New Item(11, "efh"), New Item(12, "bcd")})
Dim t = From i In items
Select New With {i.Name, items.Where(Function(x) x.ID <= i.ID AndAlso x.Name = i.Name).Count()}
For Each t2 In t
Console.WriteLine(t2.Name ", " t2.Count.ToString())
Next
Console.ReadLine()
End Sub
В качестве пояснения. Я инициализирую список элементов типа, используя ваши значения, но с дополнительным полем, которое я могу использовать для упорядочения. Затем я использую LINQ для создания анонимного типа, содержащего имя плюс количество всех вхождений имени вплоть до текущей позиции в списке; это делается путем сопоставления имени и включения всех идентификаторов, меньших или равных текущему идентификатору.
Затем я выполняю итерацию по анонимному списку, записываемому на консоль. Консоль.Строка чтения добавляется в конце, чтобы убедиться, что у меня есть возможность прочитать значения, прежде чем окно консоли закроется!
Предполагая, что у ваших данных есть некоторый столбец, который вы можете упорядочить, вы должны быть в состоянии адаптировать это в соответствии с вашими целями.
Комментарии:
1. это добавленное вами дополнительное числовое поле и есть фактический результат . поэтому я не уверен, как / почему вы добавляете его изначально
2. Нет, это не так. Если вы проверите мой код, вы увидите, что добавленное мной дополнительное поле является уникальным идентификатором от 1 .. 12. Это не отображается в выходных данных. Вместо этого он используется для упорядочивания элементов, чтобы текущее количество было правильным. Если вы проверите мой вывод, вы увидите, что на выходе указаны текущие итоги, которые вы хотите, а не идентификаторы. Пожалуйста, прочитайте мой ответ более внимательно.