Home > Software engineering >  How to remove an element of List<int[]> in C#
How to remove an element of List<int[]> in C#

Time:02-05

I'm trying to program a Sudoku solver in C# in which I'm using a List of an integer array with all the positions of empty fields, because I need them in my algorithm. In the progress of solving the Sudoku I need to remove those positions which got filled with a number. But somehow my list with empty positions does not get smaller, when I use the Remove-method. I will explain my problem with a simplified example:

        List<int[]> test1 = new List<int[]>();
        test1.Add(new int[] { 0, 0 });
        test1.Add(new int[] { 1, 7 });

        test1.Remove(new int[] { 1, 7 });

The first line generates the list with my one dimensional integer array (which always consists of two values - one for the column and one for the row-number). The empty positions get added in a method, but in this example I just added them in these two lines. Later on in my algorithm, I want to remove elements by using the Remove-function similarly to the Add-function. It throws no errors, even while compiling. However, it's not removing anything. I tried using the RemoveAll-method, although I don't really understand, how it works and therefore didn't find a correct solution for my problem.

By trying out a List of integers (not an integer array) the Remove-method works perfectly, but in the case of an array it doesn't seem to work this way. Even creating a seperat variable rem

        int[] rem = new int[] { 1, 7 };
        test1.Remove(rem);

does not work. I'm a beginner so I don't really know if a List of arrays is the best solution in my case.

CodePudding user response:

The reason you're not able to remove items from your list using the Remove method is that you're storing reference types in the List, but creating new references when trying to remove an item. Because reference types by default use a reference comparison (not a comparison of their fields) to determine equality, you won't be able to remove items in that way.

One way to resolve this is to create a reference to each object in the List<int[]> outside of the list creation itself. This way, you can use the existing reference as an argument to the Remove method, and, because it's referring to the same object that was added to the list, it will match and be removed:

// Here we have 'item1' and 'item2' that refer to the location of different int[]
int[] item1 = new int[] { 0, 0 };
int[] item2 = new int[] { 1, 7 };

// And now we use those references to add the int[] items to our list
List<int[]> test1 = new List<int[]>();
test1.Add(item1);
test1.Add(item2);

// Finally, we can remove an item using the same reference that we used to add it
test1.Remove(item2);

This is very clunky, however, since we now need to maintain an individual reference for every item in our list as well as the list itself.

Another way to resolve this would be to search for the item we want to remove using our own equality algorithm (rather than relying on the default equality that Remove uses). We can use FirstOrDefault to search for the first item that has a length of 2 and whose values match those that we want. It will return a reference to the item if it's found, or null if it's not found. We can use IndexOf to get the index of the item (or -1 if it's not found), and then pass that index to the RemoveAt method to remove it:

List<int[]> test1 = new List<int[]>();
test1.Add(new int[] { 0, 0 });
test1.Add(new int[] { 1, 7 });

int indexToRemove = test1.IndexOf(test1.FirstOrDefault(item => 
    item.Length == 2 && item[0] == 1 && item[1] == 1));

if (indexToRemove >= 0) test1.RemoveAt(indexToRemove);

As you can see, what you're trying to do isn't super easy. As a suggestion to help you think about the problem in a different way, you might consider using a 2-dimensional array to store the sudoku grid. Normally we store the row in the first dimesion and the column in the second dimension:

int[,] grid = new int[9, 9];

You could potentially create a few of these, one to represent the puzzle, one to represent the user's guesses, one to store user's "notes" (if you allow them to tag a cell with possible values before committing to a guess).

Then the typical way to loop through the grid would be something like:

for (int row = 0; row < 9; row  )
{
    for (int col = 0; col < 9; col  )
    {
        // Do something with the cell at 'row' 'col' here

        // Set a value for this cell
        grid[row, col] = row   col;

        // Report the value of a cell
        Console.WriteLine($"The value at row {row} and column {col} is {grid[row, col]}");
    }
}

CodePudding user response:

bool IntArrayPredicate(int[] element)
{
    return element.SequenceEqual(new int[] { 2, 3 });
}

List<int[]> listOfIntArray = new List<int[]>();

listOfIntArray.Add(new int[] { 0, 0 });
listOfIntArray.Add(new int[] { 1, 7 });
listOfIntArray.Add(new int[] { 2, 3 });

listOfIntArray.RemoveAll(element => element.SequenceEqual(new int[] { 1, 7 })); //It Works!. Using lambda expresion. Remove by Comparing sequences that match equals.

int[] toRemove = listOfIntArray[0];
listOfIntArray.Remove(toRemove); //It works!. Remove element by exact reference.

listOfIntArray.Remove(new int[] { 2, 3 }); // Not working / References are different.

listOfIntArray.RemoveAll(IntArrayPredicate); // It works!. Same as using lambda but using method reference.
            
Console.WriteLine($"{nameof(listOfIntArray)} has {listOfIntArray.Count()} elements"); // Yup. 0 elements.

CodePudding user response:

you need to write a custom equality comparer that checks if two arrays are equal based on their values, not their references

class Program
{
    static void Main(string[] args)
    {
        List<int[]> test1 = new List<int[]>(new ArrayComparer());
        test1.Add(new int[] { 0, 0 });
        test1.Add(new int[] { 1, 7 });

        test1.Remove(new int[] { 1, 7 });
        Console.WriteLine(test1.Count); // Output: 1
    }
}

And ArrayComparer

class ArrayComparer : IEqualityComparer<int[]>
{
    public bool Equals(int[] x, int[] y)
    {
        if (x == null || y == null)
            return false;
        if (x.Length != y.Length)
            return false;
        for (int i = 0; i < x.Length; i  )
        {
            if (x[i] != y[i])
                return false;
        }
        return true;
    }

    public int GetHashCode(int[] obj)
    {
        int result = 17;
        for (int i = 0; i < obj.Length; i  )
        {
            unchecked
            {
                result = result * 23   obj[i];
            }
        }
        return result;
    }
}

  • Related