2016-07-25 2 views
4

Извинения, если я пропущу что-то очень основное.Коллекция группы LINQ произвольной решеткой

Для заданного массива решетки, в котором значения решетки представляют собой минимальное значение для их ведра, что является наилучшим способом группировки массива значений.

например.

double[] lattice = { 2.3, 2.8, 4.1, 4.7 }; 
double[] values = { 2.35, 2.4, 2.6, 3, 3.8, 4.5, 5.0, 8.1 }; 

GroupByLattice(values, lattice); 

таким образом, что GroupByLattice возвращает IGroupings, которые выглядят как:

2.3 : { 2.35, 2.4, 2.6 } 
2.8 : { 3, 3.8 } 
4.1 : { 4.5 } 
4.7 : { 5.0, 8.1 } 

редактировать:

Я достаточно зеленый с LINQ запросов, что это лучшее, что я могу некоторые с:

values.GroupBy(curr => lattice.First(lat => curr > lat)) 

Вопросы с этим:

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

посмертного Решение и результаты:

Дмитрий Быченко предоставил большой ответ, я просто хотел обеспечить некоторые последующие действия для тех, кто может столкнуться с этим ответом в будущем. Я изначально пытаюсь решить: How can I simplify a huge dataset for plotting?

Для начала моя первая попытка была на самом деле довольно близкой. С моей решетки будучи уже заказал мне просто нужно изменить .First(...) к .Last(...)

т.е.

values.GroupBy(curr => lattice.Last(lat => curr > lat)) 

Это все хорошо, но было любопытно, насколько лучше решение Дмитрия бы выполнить. Я тестировал его со случайным набором из 10000 удвоений, с решеткой с шагом 0,25. (Я вытащил .Select(...) трансформироваться из раствора Дмитрия держать его справедливым)

В среднем 20 трасс выплюнуть результат:

Mine: 602ms 
Dmitrys: 3ms 

Мм ... WOW! Это 200-кратное увеличение скорости. 200x! Я должен был запустить это несколько раз и проверить в отладчике, чтобы быть уверенным, что оператор LINQ оценивал до отметки времени (Trusty .ToArray() на помощь).Я собираюсь сказать сейчас, кто ищет для выполнения этой же задачи следует, безусловно, использовать эту методологию

ответ

5

При условии, что lattice является отсортирован (это легко отсортировать массив с Array.Sort(lattice)), вы можете использовать Array.BinarySearch:

double[] lattice = { 2.3, 2.8, 4.1, 4.7 }; 
    double[] values = { 2.35, 2.4, 2.6, 3, 3.8, 4.5, 5.0, 8.1 }; 

    var result = values 
    .GroupBy(item => { 
     int index = Array.BinarySearch(lattice, item); 

     return index >= 0 ? lattice[index] : lattice[~index - 1]; 
    }) 
    .Select(chunk => String.Format("{0} : [{1}]", 
     chunk.Key, String.Join(", ", chunk))); 

Тест

Console.Write(String.Join(Environment.NewLine, result)); 

Результат

2.3 : [2.35, 2.4, 2.6] 
    2.8 : [3, 3.8] 
    4.1 : [4.5] 
    4.7 : [5, 8.1] 
+0

Вам нужно сделать инверсию для не точных совпадений * до * группировки, в противном случае точное совпадение приведет к тому, что оно будет в своей группе, а не в группе для этого матча. Это также то, что реалистично должно быть выведено в именованный метод, вместо того, чтобы пытаться сделать что-то подобное встроенным. – Servy

+0

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

+1

@Servy: Я вижу, спасибо! При работе с * плавающей запятой * точное равенство - это случай, который можно легко контролировать. –

0

Если вы когда-нибудь это нужно быстрее, вы можете перебирать массивы только один раз, если оба из них отсортированные:

double[] lattice = { 2.3, 2.8, 4.1, 4.7 }; 
double[] values = { 2.35, 2.4, 2.6, 3, 3.8, 4.5, 5.0, 8.1 }; 

var result = new List<double>[lattice.Length]; // array of lists 

for (int l = lattice.Length - 1, v = values.Length - 1; l >= 0; l--) // starts from last elements 
{ 
    result[l] = new List<double>(values.Length/lattice.Length * 2); // optional initial capacity of the list 

    for (; v >= 0 && values[v] >= lattice[l]; v--) 
    { 
     result[l].Insert(0, values[v]); 
    } 
} 
Смежные вопросы