Searching a list of an int arrays into another list of int arrays We are given two list on integer arrays, and the objective is to retrieve another list which contains the duplicates in both lists.

The code developed is the following:

public List&lt;int[]&gt; GetDuplicates(List&lt;int[]&gt; pInputList1, List&lt;int[]&gt; pInputList2)
{
    var outputList = new List&lt;int[]&gt;();

    pInputList2.ForEach(x =&gt;
    {
        outputList.AddRange(pInputList1.Select(j =&gt; j).Where(y =&gt;
        {
            for (int i = 0; i &lt; y.Length; i++)
                if (y[i] != x[i])
                    return false;
            return true;
        }));
    });

    return outputList;
} 


The code works as intended what I am concerned about is optimization, how to improve this code in both, execution time and stability.

What I am doing in this code is compare each element from one list with ALL of the elements of the other list (I see a problem here, but don't know how to approach it well, the foreach seems not to be the best option here).
The original code:

public List&lt;int[]&gt; GetDuplicates(List&lt;int[]&gt; pInputList1, List&lt;int[]&gt; pInputList2)
{
    var outputList = new List&lt;int[]&gt;();

    pInputList2.ForEach(x =&gt;
    {
        outputList.AddRange(pInputList1.Select(j =&gt; j).Where(y =&gt;
        {
            for (int i = 0; i &lt; y.Length; i++)
                if (y[i] != x[i])
                    return false;
            return true;
        }));
    });

    return outputList;
} 


I'd start with a few readability simplifications first:


Select(j =&gt; j) is a no-op and can be removed
The usage of ForEach is hurting readability (and performance, due to passing delegates around) a little bit
Moving the pInputList1.Where(...) outside of the AddRange also makes it clearer what's going on.




public List&lt;int[]&gt; GetDuplicates(List&lt;int[]&gt; pInputList1, List&lt;int[]&gt; pInputList2)
{
    var outputList = new List&lt;int[]&gt;();

    foreach (var x in pInputList2)
    {
        var duplicates = pInputList1.Where(y =&gt;
        {
            for (int i = 0; i &lt; y.Length; i++)
                if (y[i] != x[i])
                    return false;
            return true;
        });

        outputList.AddRange(duplicates);
    }

    return outputList;
}


Next, as mentioned in the comments, the intention is to filter the second list according to whether an identical array appears in the first list. The method and its arguments (and derived local variables) should be renamed to reflect that. I'd also swap the parameters over, because pInputList2 is the important part of the method, while pInputList1 is the set against which pInputList2 is checked.

public List&lt;int[]&gt; FilterByWhitelist(List&lt;int[]&gt; testArrays, List&lt;int[]&gt; whiteListedArrays)
{
    var outputList = new List&lt;int[]&gt;();

    foreach (var testArray in testArrays)
    {
        var duplicates = whiteListedArrays.Where(whiteListedArray =&gt;
        {
            for (int i = 0; i &lt; whiteListedArray.Length; i++)
                if (whiteListedArray[i] != testArray[i])
                    return false;
            return true;
        });

        outputList.AddRange(duplicates);
    }

    return outputList;
}


Using the same point in the comments: we don't need to find every matching array in whiteListedArrays - we only need to know whether there is one. As such, we can use Any instead of Where:

public List&lt;int[]&gt; FilterByWhitelist(List&lt;int[]&gt; testArrays, List&lt;int[]&gt; whiteListedArrays)
{
    var outputList = new List&lt;int[]&gt;();

    foreach (var testArray in testArrays)
    {
        var isWhiteListed = whiteListedArrays.Any(whiteListedArray =&gt;
        {
            for (int i = 0; i &lt; whiteListedArray.Length; i++)
                if (whiteListedArray[i] != testArray[i])
                    return false;
            return true;
        });

        if (isWhiteListed)
        {
            outputList.Add(testArray);
        }
    }

    return outputList;
}


Another bug in this implementation is: what if one of your testArrays is longer than one of the whiteListedArrays? You're accessing x[i] while only ensuring that i is less than the length of y. You'll probably get an ArgumentOutOfRangeException from this. Because two arrays can't be equal if their lengths don't match, this should be checked first. 

On the other hand, Linq has a nice built-in method for checking whether two sequences are equal:

public List&lt;int[]&gt; FilterByWhitelist(List&lt;int[]&gt; testArrays, List&lt;int[]&gt; whiteListedArrays)
{
    var outputList = new List&lt;int[]&gt;();

    foreach (var testArray in testArrays)
    {
        var isWhiteListed = whiteListedArrays.Any(
            whiteListedArray =&gt; testArray.SequenceEqual(whiteListedArray));

        if (isWhiteListed)
        {
            outputList.Add(testArray);
        }
    }

    return outputList;
}


This method now iterates once over its main argument, and returns a subsequence of that argument. This sounds like an ideal scenario for an extension method on IEnumerable.

public static IEnumerable&lt;int[]&gt; FilterByWhitelist(this IEnumerable&lt;int[]&gt; testArrays, List&lt;int[]&gt; whiteListedArrays)
{
    foreach (var testArray in testArrays)
    {
        var isWhiteListed = whiteListedArrays.Any(
            whiteListedArray =&gt; testArray.SequenceEqual(whiteListedArray));

        if (isWhiteListed)
        {
            yield return testArray;
        }
    }
}


This would be called like:

var originalList = new List&lt;int[]&gt; { ... };
var whiteList = new List&lt;int[]&gt; { ... };

var filteredList = originalList.FilterByWhitelist(whiteList).ToList();


One final performance improvement I can see is pre-grouping the whiteListedArrays by length, and only testing for sequence equality where the lengths are already known to be equal:

public static IEnumerable&lt;int[]&gt; FilterByWhitelist(this IEnumerable&lt;int[]&gt; testArrays, List&lt;int[]&gt; whiteListedArrays)
{
    var lengthGroupedWhiteListedArrays = whiteListedArrays
        .GroupBy(arr =&gt; arr.Length)
        .ToDictionary(group =&gt; group.Key, group =&gt; group.ToList());

    foreach (var testArray in testArrays)
    {
        List&lt;int[]&gt; lengthMatchedWhiteListedArrays;

        if (!lengthGroupedWhiteListedArrays.TryGetValue(testArray.Length, out lengthMatchedWhiteListedArrays))
        {
            continue;
        }

        var isWhiteListed = lengthMatchedWhiteListedArrays.Any(
            whiteListedArray =&gt; testArray.SequenceEqual(whiteListedArray));

        if (isWhiteListed)
        {
            yield return testArray;
        }
    }
}


At this point, we're only iterating over whiteListedArrays once, so that too can become an IEnumerable:

public static IEnumerable&lt;int[]&gt; FilterByWhitelist(this IEnumerable&lt;int[]&gt; testArrays, IEnumerable&lt;int[]&gt; whiteListedArrays)
{
    var lengthGroupedWhiteListedArrays = whiteListedArrays
        .GroupBy(arr =&gt; arr.Length)
        .ToDictionary(group =&gt; group.Key, group =&gt; group.ToList());

    foreach (var testArray in testArrays)
    {
        List&lt;int[]&gt; lengthMatchedWhiteListedArrays;

        if (!lengthGroupedWhiteListedArrays.TryGetValue(testArray.Length, out lengthMatchedWhiteListedArrays))
        {
            continue;
        }

        var isWhiteListed = lengthMatchedWhiteListedArrays.Any(
            whiteListedArray =&gt; testArray.SequenceEqual(whiteListedArray));

        if (isWhiteListed)
        {
            yield return testArray;
        }
    }
}


We could, at this point, extract the array-specific code into a class that encapsulates the whitelist:

public class Whitelist
{
    private readonly IReadOnlyDictionary&lt;int, List&lt;int[]&gt;&gt; _lengthGroupedWhiteListedArrays;

    public Whitelist(IEnumerable&lt;int[]&gt; whitelistedArrays)
    {
        _lengthGroupedWhiteListedArrays= whitelistedArrays
            .GroupBy(arr =&gt; arr.Length)
            .ToDictionary(group =&gt; group.Key, group =&gt; group.ToList());
    }

    public bool Contains(int[] item)
    {
        List&lt;int[]&gt; correctLengthFilterArrays;

        if (!_lengthGroupedWhiteListedArrays.TryGetValue(item.Length, out correctLengthFilterArrays))
        {
            return false;
        }

        return correctLengthFilterArrays.Any(filterArray =&gt; item.SequenceEqual(filterArray));
    }
}

...

public static IEnumerable&lt;int[]&gt; FilterByWhitelist(this IEnumerable&lt;int[]&gt; testArrays, Whitelist whitelist)
{
    foreach (var testArray in testArrays)
    {
        if (whitelist.Contains(testArray))
        {
            yield return testArray;
        }
    }
}


Although by doing this, FilterByWhitelist has more or less become Linq's Where:

var originalList = new List&lt;int[]&gt; { ... };
var whiteList = new Whitelist(new List&lt;int[]&gt; { ... });

var filteredList = originalList
    .Where(arr =&gt; whiteList.Contains(arr))
    .ToList();


This way, the pre-grouping of array lengths can be used in many places.