Консолидация и фильтрация элементов iList, поиск паттернов, методов или существующих методов - PullRequest
0 голосов
/ 11 сентября 2010

Я читаю двоичный файл в список связывания (t);быть привязанным к представлению данных.
Каждая строка в файле представляет отдельную транзакцию, но мне нужно объединить и / или отфильтровать транзакции, которые соответствуют определенным критериям.

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

Я не хочу изобретать велосипед, если у меня его тоже нет.В некоторых случаях меня особенно беспокоят проблемы со скоростью и производительностью при обработке записей объемом более 100 тыс.

1007 *. В настоящее время работает с .NET 2.0, но перейдет на версию 3.5, если существует особенно привлекательное решение.

Обновление Я изменил решение на 3,5, так что это больше не проблема.Я должен был указать, что этот проект VB.NET, но я могу добавить новую библиотеку C # для этой конкретной функции, чтобы использовать преимущества итераторов C #.

Ответы [ 2 ]

1 голос
/ 11 сентября 2010

Да, вы хотите 3.5, потому что это дает вам LINQ - интегрированный с языком запрос.

Существует небольшая производительность, но для огромных наборов записей вы можете компенсировать это с помощью PLINQ(параллельная обработка).

LINQ - это декларативный, функциональный способ работы с наборами.

Понятия, которые вам понадобятся:
- лямбда-выражения () =>
- методы расширения

Рассмотрим из набора из 10000 строк, которые вы хотите, чтобы первые 100 были длиннее 24 символов:

var result = source.Where(s => s.Length > 24).Take(100);

Из набора Person объектов, которые вы хотите вернуть имена, ноони подразделяются на firstName и lastName свойства.

var result = source.Select(person => person.firstName + person.LastName);

Возвращает IEnumerable<string>.

Из того же набора вы хотите средний возраст:

var result = source.Average(person => person.Age);

Младшие 10 человек:

var result = source.OrderBy(person => person.Age).Take(10);

Все, сгруппированные по первой букве их фамилии:

var result = source.GroupBy(person => person.lastName[0]);

Возвращает IGrouping<char, Person>

Имена25 самых старых людей, чья фамилия начинается с S:

var result = source.Where(person => person.lastName.StartsWith("S"))
   .OrderByDescending(person => person.Age)
   .Take(25)
   .Select(person => person.firstName + person.lastName);

Только представьте, сколько трескиДля этого вам нужно было бы написать в цикле foreach и сколько у вас будет места, чтобы ввести дефекты или пропущенные оптимизации в этот код.Декларативная природа синтаксиса LINQ облегчает чтение и поддержку.

Существует альтернативный синтаксис, который напоминает SQL-код, но показывает, как вы действительно определяете запросы к произвольному набору объектов.Учтите, что вы хотите получить людей, чье имя «Боб»:

var result = 
    from person in source
    where person.firstName == "Bob"
    select person;

Это выглядит странно, но это допустимый код C #, если вы подпрыгиваете с 2.0.

Мое единственное предупреждениезаключается в том, что после работы с LINQ вы можете отказаться от работы в 2.0 снова.

Для изучения синтаксиса LINQ доступно множество отличных ресурсов - это не займет много времени.


update

Дополнительные соображения в ответ на 1-й комментарий:

В вашем распоряжении уже есть очень мощный инструмент с C # 2.0 - итераторы.

Учтите:

public class Foo
{
    private IEnumerable<Record> GetRecords()
    {
        Record record = // do I/O stuff, instantiate a record

        yield return record;
    }

    public void DisplayRecords()
    {
        foreach (Record record in GetRecords())
        {
            // do something meaningful

            // display the record
        }
    }
}

Итак, что в этом замечательного?Метод GetRecords() является блоком итератора, а ключевое слово yield возвращает результаты в соответствии с запросом ("отложенная оценка").

Это означает, что при вызове DisplayRecords() онвызовет GetRecords(), но как только GetRecords() получит Record, он вернет его к DisplayRecords(), что затем может сделать с ним что-то полезное.Когда блок foreach повторяется снова, выполнение вернется к GetRecords(), что вернет следующий элемент и т. Д.

Таким образом, вам не нужно ждать 100 000 записей, чтобы бытьчитать с диска, прежде чем вы сможете начать сортировку и отображение результатов.

Это дает некоторые интересные возможности;может ли это быть полезным в вашей ситуации, решать вам (например, вы не захотите обновлять привязку сетки 100 000 раз).

0 голосов
/ 12 сентября 2010

Похоже, что вы хотите сделать что-то подобное в псевдо-LINQ: data.GroupBy().Select(Count()).Where() - вы группируете (консолидируете) по некоторым критериям, подсчитываете число в каждой группе, а затем фильтруете по результатам.

Однако вы предполагаете, что у вас может быть слишком много данных для одновременной загрузки в память, поэтому вы хотите консолидировать их при загрузке данных. Это можно сделать с помощью вашего собственного оператора GroupByCount, чем-то вроде этой упрощенной версии:

public static IEnumerable<KeyValuePair<T, int>>
    GroupByCount<T>(IEnumerable<T> input)
{
    Dictionary<T, int> counts = new Dictionary<T, int>();
    foreach (T item in input)
        if (counts.ContainsKey(item))
            counts[item]++;
        else
            counts[item] = 1;
    return counts;
}

Тогда у вас просто будет data.GroupByCount().Where(), и все ваши данные будут консолидированы при загрузке, поскольку foreach будет загружать только следующий элемент после обработки предыдущего.

...