Поиск похожих записей в LINQ - PullRequest
3 голосов
/ 13 января 2012

У меня есть следующий запрос LINQ, который будет использоваться для поиска любых «похожих» грузов:

from c in cons
group c by new { c.TripDate.Value, c.DeliveryPostcode, c.DeliveryName } into cg
let min = cg.Min(a => a.DeliverFrom)
let max = cg.Max(a => a.DeliverFrom)
let span = max - min
where span.TotalMinutes <= 59
select cg;

Главное - это мин, макс и диапазон. По сути, любые грузы, находящиеся в «группе» и имеющие дату и время DeliverFrom в течение 59 минут по сравнению с любой другой в группе, будут возвращены в группу.

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

Как бы я поступил так?

РЕДАКТИРОВАТЬ: Doh, еще один пункт был добавлен в этом. Есть поле под названием «Вес» и поле «Пробелы», каждая группа может иметь максимум 26 весов и 26 пробелов

Ответы [ 2 ]

3 голосов
/ 13 января 2012

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

В качестве упражнения для мысли представьте, что у вас было 3 записи: в 1:00, 1:30 и 2:00. Как бы вы хотели сгруппировать их? Либо первые два, либо последние два будут работать в группе (с интервалом менее 59 минут), но все 3 не будут работать.

Если вы просто хотите объединить элементы в группу, если они находятся в пределах 59 минут от любого другого элемента в группе, вам нужно будет продолжать выполнять итерации, пока не прекратите находить новые элементы для добавления в любой кластер.

1 голос
/ 13 января 2012

Я бы сгруппировал грузы с той же логикой, что и вы, но вместо этого использовал эту перегрузку из GroupBy, что позволило мне проецировать каждую группу грузов в другой тип.Этот тип будет здесь перечислимой последовательностью групп грузов, каждый элемент в которой представляет грузы, которые не только находились в одной и той же группе с самого начала, но также должны доставляться в течение часа.Таким образом, подпись resultSelector будет

Func<anontype, IEnumerable<Consignment>, IEnumerable<IEnumerable<Consignment>>>

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

В пределах resultSelector вам необходимо сначала отсортировать входящую группу грузов по DeliverFrom, а затем вернуть подгруппы на основев это времяТак что это может выглядеть так:

IEnumerable<IEnumerable<Consignment>>
Partitioner(ConsignmentGroupKey key, IEnumerable<Consignment> cg)
{
    cg = cg.OrderBy(c => c.DeliverFrom);
    var startTime = cg.First().DeliverFrom;
    var subgroup = new List<Consignment>();

    foreach(var cons in cg) {
        if ((cons.DeliverFrom - startTime).TotalMinutes < 60) {
            subgroup.Add(cons);
        }
        else {
            yield return subgroup;
            startTime = cons.DeliverFrom;
            subgroup = new List<Consignment>() { cons };
        }
    }

    if (subgroup.Count > 0) {
        yield return subgroup;
    }
}

Я не пробовал это, но, насколько я могу судить, это должно работать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...