Найдите все данные пересечения, не только уникальные значения

Я думал, что понял Intersect, но оказывается, что я был неправ.

 List<int> list1 = new List<int>() { 1, 2, 3, 2, 3};
 List<int> list2 = new List<int>() { 2, 3, 4, 3, 4};

 list1.Intersect(list2) =>      2,3

 //But what I want is:
 // =>  2,3,2,3,2,3,3

Я могу изобразить путь как:

 var intersected = list1.Intersect(list2);
 var list3 = new List<int>();
 list3.AddRange(list1.Where(I => intersected.Contains(I)));
 list3.AddRange(list2.Where(I => intersected.Contains(I)));

Существует ли более легкий путь в LINQ для достижения этого?

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

2,2,2,3,3,3,3 также был бы совершенно в порядке.

Проблема состоит в том, что я использую это на очень большом количестве, Таким образом, мне нужна эффективность.

Мы говорим об Объектах, не ints. ints были только для легкого примера, но я понимаю, что это может иметь значение.

12
задан BartoszKP 19 April 2017 в 10:55
поделиться

3 ответа

Давайте посмотрим, сможем ли мы точно охарактеризовать то, что вы хотите. Поправьте меня, если я ошибаюсь. Вы хотите: все элементы списка 1, в порядке, которые также появляются в списке 2, за которым следует все элементы списка 2, в порядке, которые также появляются в списке 1. Да?

кажется простым.

return list1.Where(x=>list2.Contains(x))
     .Concat(list2.Where(y=>list1.Contains(y)))
     .ToList();

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

list1set = new HashSet(list1);
list2set = new HashSet(list2);

return list1.Where(x=>list2set.Contains(x))
     .Concat(list2.Where(y=>list1set.Contains(y)))
     .ToList();

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

16
ответ дан 2 December 2019 в 20:17
поделиться
var set = new HashSet(list1.Intersect(list2));
return list1.Concat(list2).Where(i=>set.Contains(i));
1
ответ дан 2 December 2019 в 20:17
поделиться

Я не верю, что это возможно со встроенным API. Но вы можете использовать следующее, чтобы получить результат, который вы ищете.

IEnumerable<T> Intersect2<T>(this IEnumerable<T> left, IEnumerable<T> right) {
  var map = left.ToDictionary(x => x, y => false);
  foreach ( var item in right ) {
    if (map.ContainsKey(item) ) {
      map[item] = true;
    }
  }
  foreach ( var cur in left.Concat(right) ) {
    if ( map.ContainsKey(cur) ) {
      yield return cur;
    }
  }
}
-1
ответ дан 2 December 2019 в 20:17
поделиться
Другие вопросы по тегам:

Похожие вопросы: