2013-04-24 4 views
7

У меня есть следующие модели:записи списка группы с помощью LINQ

public class Entry 
{ 
    public int UseraccountId { get; set; } 
    public int CompanyId { get; set; } 
    public DateTime CreationDate { get; set; } 
    public string Target { get; set; } 
    public string Message { get; set; } 
} 

И список с большим количеством записей:

List<Entry> entries = ... //get all entries. 

Пример:

Example before grouping

I» d теперь, как строки 2 и 3, должны быть сгруппированы, потому что они имеют один и тот же UserId, тот же CompanyId, одну и ту же цель и почти (и это сложная часть), скажем, в диапазоне 5 секунд, в то же время.

После группирования моего списка должен выглядеть следующим образом:

enter image description here

Есть ли простой подход к этой проблеме? Какие-нибудь советы? Держу пари, Linq поможет мне, но я не уверен, как это сделать.

Редактировать: Благодарим вас за отзыв. Я решил изменить дизайн и убедиться, что дата-время теперь действительно то же самое. Таким образом, группировка с linq теперь очень проста.

+0

Когда вы говорите «почти», как вы хотите, чтобы это группа, в ту же минуту, те же 10 секунд? –

+0

Скажем, в диапазоне 5 секунд. – mosquito87

+3

Группировка по тем же самым значениям проста; взгляните на [многие вопросы] (http://stackoverflow.com/search?q=%5Bc%23%5D+linq+group+by+multiple+keys) на SO, связанные с этим. Группировка почти с одинаковыми значениями немного сложнее, потому что если X почти то же самое, что и Y, а Y почти то же, что и Z, то это не значит, что X почти то же самое, что и Z. [алгоритм кластеризации] (http://en.wikipedia.org/wiki/Cluster_analysis). – dtb

ответ

0

Нет прямого ответа, так как это зависит от того, что вы считаете совпадением. Есть простые, а также сложные подходы и в любом месте между ними. Вам нужно придумать алгоритм для этого. Легкий подход состоял бы в том, чтобы сбрить секунды и просто сравняться до минуты, однако это может быть слишком долго. Вы можете написать метод, который нормализует временные метки до 5 или 10 секунд и группирует их, как было предложено.

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

Если это не сработает, и вы хотите сгруппировать через искусственное обрезание, вам понадобится другой подход.Простой подход в этом случае может заключаться в том, что вы используете LINQ для группировки всем, кроме метки времени. Это сделает предварительную группировку ваших данных. Затем вы можете перебирать каждую группу и сравнивать каждое значение времени друг с другом в одной и той же группе и определять, находится ли она в пределах вашего диапазона. Затем вручную захватите те значения, которые попадают в указанный диапазон, и сгруппируйте их вместе.

У этого есть дополнительный крайный кейс, на который вы должны будете принять решение. Если вы решите, что будете группироваться в течение 1 секунды, и у вас есть три записи, чьи секунды (упрощенные) 1, 2 и 3. 1 и 2 находятся в пределах секунды, а 2 и 3 также находятся в пределах секунды, но 1 и 3 арены, т. Вы бы группировали их на основе 2 в пределах одной секунды от других, или вы бы группировали 1 и 2, делая 2 не имеющим право быть сгруппированными с 3 и 3, были бы сами по себе.

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

Вам необходимо определить, что вы подразумеваете под почти и планируете соответственно.

+0

Спасибо за ваши мысли. Это заставило меня задуматься о моем дизайне, и я решил изменить его так, чтобы время datetime теперь было таким же, и группировка теперь довольно проста. – mosquito87

0

Это даст диапазон от -5 секунд, но для полного алгоритма сопоставления (кластеризации) на CreationDate, я предполагаю, что это будет намного сложнее. Вы понимаете эту идею.

List<Entry> entries = entries.GroupBy(a => a.UserId) 
          .ThenBy(a => a.CompanyId) 
          .ThenBy(a => a.CreationDate.AddSeconds(-5)); 
+0

На самом деле, даже если это не сработает должным образом для CreationDate, вам придется сначала вырезать секунды. –

+0

Попробуйте целое деление? .ThenBy (a => TimeSpan.FromTicks (a.CreationDate.Ticks) .TotalSeconds/5); – penguat

+0

Я не думаю, что вы можете группировать группировки с помощью 'ThenBy', если только это не часть некоторой библиотеки расширения Linq. –

1

Как @dtb menitons, группировка по «закрыть» трудно, потому что вы можете в конечном итоге с большим «ведро», чем предполагалось. Например, если у вас есть 100 записей, которые создаются на расстоянии 4 секунды друг от друга, группировка элементов, находящихся в пределах 5 секунд после «следующего» элемента, поместит их все в одном ведро!

Если, однако, вы хотите раунде творящий дата ближайшего, скажем, 5 секунд, а затем группа, вы могли бы использовать:

TimeSpan ts = new TimeSpan(0, 0, 5); // 5 seconds 
entries.GroupBy(i => new { 
          UserId = i.UserId, 
          CompanyId = i.CompanyId, 
          Target = i.Target, 
          RoundedTime = DateTime.MinValue.AddTicks(
              (long)(Math.Round((decimal)i.CreationDate.Ticks/ts.Ticks) * ts.Ticks) 
             ) ; 
         )) 
     .Select(g => new { 
         UserId = g.Key.UserId, 
         CompanyId = g.Key.CompanyId, 
         Target = g.Key.Target, 
         RoundedTime = g.Key.RoundedTime, 
         Message = string.Join(", ",g.Select(i=> i.Message).ToArray()) 
         }); 

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

Смежные вопросы