Протилежність перетину ()


275

Перетин можна використовувати для пошуку збігів між двома колекціями, наприклад:

// Assign two arrays.
int[] array1 = { 1, 2, 3 };
int[] array2 = { 2, 3, 4 };
// Call Intersect extension method.
var intersect = array1.Intersect(array2);
// Write intersection to screen.
foreach (int value in intersect)
{
    Console.WriteLine(value); // Output: 2, 3
}

Однак те, що я хотів би досягти навпаки, я хотів би перерахувати предмети з однієї колекції, які відсутні в іншій :

// Assign two arrays.
int[] array1 = { 1, 2, 3 };
int[] array2 = { 2, 3, 4 };
// Call "NonIntersect" extension method.
var intersect = array1.NonIntersect(array2); // I've made up the NonIntersect method
// Write intersection to screen.
foreach (int value in intersect)
{
    Console.WriteLine(value); // Output: 4
}

13
будь ласка, підтвердьте, якщо ви хочете 4 як вихід, або 1 і 4
Øyvind Bråthen

@ oyvind-knobloch-brathen Так, мені хотілося б лише 4
Пітер Бріджер

23
Як бічна примітка, цей тип набору називається симетричною різницею .
Майк Т

19
Технічно кажучи, симетрична різниця призведе до [1, 4]. Оскільки Петро хотів лише елементів масиву2, які не є в масиві1 (тобто 4), це називається відносним доповненням (він же Set-теоретична різниця)
rtorres

Відповіді:


376

Як зазначено, якщо ви хочете отримати 4 в результаті, ви можете зробити так:

var nonintersect = array2.Except(array1);

Якщо ви хочете справжнього неперетину (також 1 і 4), тоді це слід зробити:

var nonintersect = array1.Except(array2).Union( array2.Except(array1));

Це не буде найефективнішим рішенням, але для невеликих списків це має працювати чудово.


2
що було б найкращим рішенням? Дякую!
shanabus

6
Можливо, ви можете зробити це швидше, використовуючи дві вкладені для циклів, але код буде набагато бруднішим за це. Враховуючи читанність цього, я б чітко використав цей варіант, оскільки його дуже легко читати.
Øyvind Bråthen

5
Просто сторонню точку, яку слід додати, Якщо у вас є: int [] before = {1, 2, 3}; int [] після = {2, 3, 3, 4}; і ви намагаєтеся використати Виняток, щоб знайти те, що додано до 'після', оскільки 'раніше': var diff = after.Except (раніше); 'диференція' містить 4, а не 3,4. тобто стежте за дублюючими елементами, які дають вам несподівані результати
Пол Риланд

Це було б краще? array1.AddRange (array2.Except (array1));
LBW

86

Можна використовувати

a.Except(b).Union(b.Except(a));

Або ви можете використовувати

var difference = new HashSet(a);
difference.SymmetricExceptWith(b);

2
Цікаве використання SymmetricExceptWith (), я б не подумав про такий підхід
Пітер Брідгер

SymmetricExceptWithце, мабуть, мій улюблений метод.
Еш Кларк

6
Я порівняв їх у реальній програмі, де у мене було кілька списків із приблизно 125 рядків у кожній з них. Використання першого підходу насправді швидше для списків такого розміру, хоча він здебільшого незначний, як обидва підходи, де менше половини мілісекунди.
Дан

1
Було б добре, якби для цього BCL був метод розширення Linq. Начебто упущення.
Дрю Ноакс

Хтось орієнтувався на SymmetricExceptWith і знайшов це набагато швидше: skylark-software.com/2011/07/linq-and-set-notation.html
Колін

11

Цей код перераховує кожну послідовність лише один раз і використовує Select(x => x)для приховування результату, щоб отримати чистий метод розширення у стилі Linq. Оскільки він використовує HashSet<T>час його виконання, O(n + m)якщо хеші добре розподілені. Повторювані елементи в будь-якому списку опущені.

public static IEnumerable<T> SymmetricExcept<T>(this IEnumerable<T> seq1,
    IEnumerable<T> seq2)
{
    HashSet<T> hashSet = new HashSet<T>(seq1);
    hashSet.SymmetricExceptWith(seq2);
    return hashSet.Select(x => x);
}

6

Я думаю, ви можете шукати Except:

Оператор «Виняток» виробляє задану різницю між двома послідовностями. Він поверне лише елементи в першій послідовності, які не з’являються у другій. Ви можете додатково надати власну функцію порівняння рівності.

Перегляньте це посилання , це посилання або Google для отримання додаткової інформації.


2

Я не на 100% впевнений, що має робити ваш метод NonIntersect (щодо теорії множин) - це
B \ A (все з B, що не відбувається в A)?
Якщо так, то ви повинні мати можливість використовувати операцію «Крім» (B.Except (A)).


Перетин множин == A∪B \ A∩B
амуліарний

2
/// <summary>
/// Given two list, compare and extract differences
/// http://stackoverflow.com/questions/5620266/the-opposite-of-intersect
/// </summary>
public class CompareList
{
    /// <summary>
    /// Returns list of items that are in initial but not in final list.
    /// </summary>
    /// <param name="listA"></param>
    /// <param name="listB"></param>
    /// <returns></returns>
    public static IEnumerable<string> NonIntersect(
        List<string> initial, List<string> final)
    {
        //subtracts the content of initial from final
        //assumes that final.length < initial.length
        return initial.Except(final);
    }

    /// <summary>
    /// Returns the symmetric difference between the two list.
    /// http://en.wikipedia.org/wiki/Symmetric_difference
    /// </summary>
    /// <param name="initial"></param>
    /// <param name="final"></param>
    /// <returns></returns>
    public static IEnumerable<string> SymmetricDifference(
        List<string> initial, List<string> final)
    {
        IEnumerable<string> setA = NonIntersect(final, initial);
        IEnumerable<string> setB = NonIntersect(initial, final);
        // sum and return the two set.
        return setA.Concat(setB);
    }
}

2

array1.NonIntersect (array2);

Такого оператора, що не перетинає, немає в Linq, який ви повинні робити

крім -> союз -> крім

a.except(b).union(b.Except(a));

-1
string left = "411329_SOFT_MAC_GREEN";
string right= "SOFT_MAC_GREEN";

string[] l = left.Split('_');
string[] r = right.Split('_');

string[] distinctLeft = l.Distinct().ToArray();
string[] distinctRight = r.Distinct().ToArray();

var commonWord = l.Except(r, StringComparer.OrdinalIgnoreCase)
string result = String.Join("_",commonWord);
result = "411329"
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.