Получить одну строку из повторяющихся столбцов на основе другого столбца - PullRequest
0 голосов
/ 26 марта 2019

Допустим, у меня есть эта таблица / IQueriable

+------+------+------+------------+-------------+
| col1 | col2 | col3 | grouperCol | selectorCol |
+------+------+------+------------+-------------+
|    1 | John | Doe  | mail1      |             |
|    1 | John | Doe  | mail2      |           1 |
|    1 | John | Doe  | mail3_x    |             |
|    2 | Bob  | Ross | mail1      |           1 |
|    2 | Bob  | Ross | mail2_x    |             |
|    2 | Bob  | Ross | mail3_x    |             |
|    3 | Jane | Doe  | mail1      |             |
|    3 | Jane | Doe  | mail2      |             |
|    3 | Jane | Doe  | mail3      |             |
+------+------+------+------------+-------------+

И я хочу получить такой результат:

+------+------+------+------------+-------------+
| col1 | col2 | col3 | grouperCol | selectorCol |
+------+------+------+------------+-------------+
|    1 | John | Doe  | mail2      |           1 |
|    2 | Bob  | Ross | mail1      |           1 |
|    3 | Jane | Doe  | mail1      |             |
+------+------+------+------------+-------------+

По сути, мне нужно сохранить одну строку, выбирая строки, в которых значение selectorCol не равно нулю ИЛИ первое.

Как мне это сделать в c #?

Мне, вероятно, нужно сделать что-то вроде

var filtered =  context.table.GroupBy(x => x.col1).Where(... 

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

Я мог бы создать новый список с помощью foreach или чего-то еще, но я думаю, что это можно сделать одной строкой?

Спасибо!

Ответы [ 3 ]

0 голосов
/ 26 марта 2019

Вот ваш однострочный текст:

.GroupBy(x => x.col1, (k, g) => g.FirstOrDefault(x => x.selectorCol == 1) ?? g.FirstOrDefault())

Но мне любопытно, какой запрос БД будет генерироваться.Вероятно, сокращение группы будет выполнено в памяти.

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

.OrderBy(x => x.selectorCol == null)
.GroupBy(x => x.col1, (k, g) => g.FirstOrDefault())
0 голосов
/ 26 марта 2019

По сути, мне нужно сохранить одну строку, выбирая строки, в которых значение selectorCol не равно нулю ИЛИ не первое.

Вы прямо не сказали этого, но я предполагаю, что если две строки имеют одинаковые Col1, то они также имеют одинаковые Col2 и Col3

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

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

Поскольку GroupBy ничего не гарантирует в отношении сохранения первоначального заказа, мы должны помнить индекс оригинальных предметов.

  • Выберите, где вы помните индекс исходного элемента
  • Затем создайте группы элементов с одинаковым значением для Col1
  • Из каждой группы сохраните элементы, которые имеют ненулевое значение для SelectorCol
  • Затем возьмите тот, у которого самый низкий индекс.

.

// first remember the original index
var result = myRows.Select( (row, index) => new
{
    Index = index
    Row = row,
}
// Then make groups of rows with same value for Col1
.GroupBy(selectResult => selectResult.Row.Col1,

// Parameter resultSelector: get the key of each group (= common Col1 value)
// and all rows that have this Col1 value
// keep only the groupElements that have a non-null value for SelectorCol
(col1, rowsWithThisCol1) => rows.WithThisCol1
     .Where(groupElement => groupElement.Row.SelectorCol != null)

     // from the remaining rows, keep the one with the lowest index
     .OrderBy(groupElement => groupElement.Index)

     // we don't need the Index anymore, select only the Row
     .Select(groupElement => groupElement.Row)

     // and keep the first:
     .FirstOrDefault();

Несмотря на то, что это работает, упорядочивать все элементы группы, если вам нужен только элемент с наименьшим индексом, - пустая трата времени. Используйте Aggregate, если вы хотите перечислить только один раз. Так что вместо OrderBy:

.Aggregate((groupElementWithLowestIndex, groupElement) =>
    // if the index of groupElement is lower,
    // then that element becomes the one with the lowest index

    (groupElement.Index < groupElementWithLowestIndex.Index) ?
     groupElement : groupElementWithLowestIndex)

// result: the one and only groupElement with the lowest index
// note: you are certain that no group is empty! So there is always one with lowest index
// get rid of the index, keep only the Row
.Row;
0 голосов
/ 26 марта 2019

Если вы хотите сделать это, основываясь только на col1, тогда:

var result = context.table.GroupBy(x => x.col1)
    .Select(g => g.FirstOrDefault(x =>selectorCol != null)??g.First());

для имени и фамилии (col1, col2);

var result = context.table.GroupBy(x => {x.col1, x.col2})
    .Select(g => g.FirstOrDefault(x =>selectorCol != null)??g.First());
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...