Я думал, что понял 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 были только для легкого примера, но я понимаю, что это может иметь значение.
Давайте посмотрим, сможем ли мы точно охарактеризовать то, что вы хотите. Поправьте меня, если я ошибаюсь. Вы хотите: все элементы списка 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();
, что только делает пару тысяч сравнений, но потенциально использует больше памяти.
var set = new HashSet(list1.Intersect(list2));
return list1.Concat(list2).Where(i=>set.Contains(i));
Я не верю, что это возможно со встроенным 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;
}
}
}