Finding which items have at least one common value with other items I have the following collection :

var items = new[] 
{
    new { Name = "A", Values = new [] { 20, 4, 5, 9, 3, 22 } },
    new { Name = "B", Values = new [] { 10, 7, 9, 8, 42 } },
    new { Name = "C", Values = new [] { 11, 103, 0 } },
    new { Name = "D", Values = new [] { 7, 35, 42 } }
};


I would like to find which items have at least one common integer value with the others items. For example, "A" has 9 in common with "B", so "A" and "B" are related each other.

At the end, this should be the result :

A =&gt; [B]
B =&gt; [A, D]
C =&gt; []
D =&gt; [B]


I wrote this code :

var tuples = items 
    .SelectMany(x =&gt; x.Values, (x, y) =&gt; new { x.Name, Value = y })
    .ToList();  

var result = tuples.Join(tuples, 
           x =&gt; x.Value, 
           x =&gt; x.Value, 
           (x, y) =&gt; new { NameA = x.Name, NameB = y.Name })        
    .Distinct()
    .GroupBy(x =&gt; x.NameA, x =&gt; x.NameB, 
        (a, b) =&gt; new { Name = a, Overlaps = b.Where(x =&gt; x != a)) });


It works (and return expected results). I was wondering if there would be a more efficient (faster and with a lower memory footprint) way do to it. I wanted to use GroupJoin() but since I need to filter out duplicate values (using Distinct) I used Join() instead.
For set-based operations like this it's better to use HashSet&lt;T&gt;.


  The HashSet&lt;T&gt; class provides high-performance set operations.


The items can easily be converted to ones containing HashSets. Then, the Overlaps method does the comparison:

var hashed = items
    .Select(i =&gt; new { Name = i.Name, Values = i.Values.ToHashSet() })
    .ToList();
var overlaps = hashed.Select(h1 =&gt; new
{
    h1.Name,
    Overlaps = hashed
        .Where(h2 =&gt; h2.Name != h1.Name &amp;&amp; h2.Values.Overlaps(h1.Values))
        .Select(h =&gt; h.Name)
});


Result:

A =&gt; B 
B =&gt; A,D 
C   
D =&gt; B 


Of course, if possible, it would be better to create items with HashSets at the outset.

EDIT

Triggered by your comment I did some benchmarking. I blew up your array a 1000 times, keeping unique names and then just measured both methods, using Linqpad:

var items = Enumerable.Range(1,1000).SelectMany(e =&gt;  new[]
{
    new { Name = "A" + e, Values = new [] { 20, 4, 5, 9, 3, 22 } },
    new { Name = "B" + e, Values = new [] { 10, 7, 9, 8, 42 } },
    new { Name = "C" + e, Values = new [] { 11, 103, 0 } },
    new { Name = "D" + e, Values = new [] { 7, 35, 42 } }
});

var sw = Stopwatch.StartNew();
var hashed = items.Select(i =&gt; new { Name = i.Name, Values = i.Values.ToHashSet() }).ToList();

sw.Elapsed.Dump();

var overlaps = hashed.Select(h1 =&gt; new
{
    h1.Name,
    Overlaps = hashed
        .Where(h2 =&gt; h2.Name != h1.Name &amp;&amp; h2.Values.Overlaps(h1.Values))
        .Select(h =&gt; h.Name).ToList()
}).ToList();
sw.Stop();
sw.Elapsed.Dump();

sw.Restart();
var tuples = items
    .SelectMany(x =&gt; x.Values, (x, y) =&gt; new { x.Name, Value = y })
    .ToList();

sw.Elapsed.Dump();
var result = tuples.Join(tuples,
           x =&gt; x.Value,
           x =&gt; x.Value,
           (x, y) =&gt; new { NameA = x.Name, NameB = y.Name })
    .Where(x =&gt; x.NameA != x.NameB)
    .Distinct()
    .GroupBy(x =&gt; x.NameA, x =&gt; x.NameB)
    .ToList();
sw.Stop();
sw.Elapsed.Dump();



The results:

00:00:00.0035664
00:00:02.0326684
00:00:00.0029370
00:00:06.3872002


As you see, the preparatory actions for both methods are done in 'no time', but the hashset-based method is considerably faster. Interestingly, if I blow up the arry 2000 times, the hashset-based method takes 7.7s and your method throws an OutOfMemoryException on my box.

Side note: if I don't make the names unique both methods are comparable. (On which I based my previous comment).