0
\$\begingroup\$

I'm currently trying to make a crafting system. For a little background I have a json file with crafting recipes. I check each of these recipes against the items I have in another list which holds the 4 items currently selected for crafting.

My current method is as follows:

private bool DoListsMatch(List<int> list1, List<int> list2)
{
    return list1.TrueForAll(list2.Contains) && list2.TrueForAll(list1.Contains);
}

At first this method looks like it's working. However, when I change the last item in the crafting window it still returns a result when it shouldn't. This suggests to me that the method is not working.

Is there any other ways of checking if lists match completely?

\$\endgroup\$
2
\$\begingroup\$

The current solution works in a specific context, but there is further room for potential error. Without sorting the list, we should not assume a specific order; even if we have set it up in that way. Unaccounted logic or later changes may alter the order in one of the lists, which results in a false negative when comparing the elements of each list using a single index value.

The solution is fairly simple. Just sort the list, before you compare values in it. This is straight forward, if we are using List<int>; int values already know how to sort themselves, in a list. If we ever want the same functionality from a List<t> of custom types, we need to make use of the System.IComparable<in t> interface.


Sorting a List<t>

We sort a List<t> with the method List<t>.Sort(). Sorting both lists before comparing values guarantees that if the lists both contain the same set of values, they will also be in the same order.

private bool DoListsMatch(List<int> list1, List<int> list2)
{
    var areListsEqual = true;

    if (list1.Count != list2.Count)
        return false;

    list1.Sort(); // Sort list one
    list2.Sort(); // Sort list two

    for (var i = 0; i < list1.Count; i++)
    {
        if (list2[i] != list1[i])
        {
            areListsEqual = false;
        }
    }

    return areListsEqual;
}

Using System.IComparable<in t> to make custom types compatible with List<t>.Sort()

Should we want to perform the same function on a list that contains custom types, we need to add the System.IComparable<in t> interface to that type. This interface only contains one method that needs to be implemented in the class - public int CompareTo(t other). This method compares the local type with the other type, and returns an int value representing the the placement of the local type in comparison to the other type.

For the example, I will simply use a custom class that stores an int. You can set this CompareTo() method up in any way you like, specific to the types you use inside that class. You may want to compare ID numbers, or compare distances to a particular position, or even a given name.

class MyInt : IComparable<MyInt>
{
    int value;

    public int CompareTo(MyInt other)
    {
        return value.CompareTo(other.value);
    }
}

private bool DoListsMatch(List<MyInt> list1, List<MyInt> list2)
{
    // ...
}

As a final note, with the use of a custom implementation of int CompareTo(t other), it is worth considering the possibility of error if two unique t values can still compare with each-other as equal. If the same two values would not return a value of true when compared as valueA == valueB1, there is still a possibility of incorrect output, when comparing both lists. If CompareTo() returns a value of 0, the items will not sort in any particular order (in relation to eachother), in the list. As such, we can not guarantee the order of the list1.

1 If the two items also return true when compared using the == operator, the previous checks will still correctly identify the objects as equal, and this will be a non-event.


\$\endgroup\$
  • \$\begingroup\$ Thanks for a very well explained answer and you're right. I shall update my solution to include the sorting of the lists. \$\endgroup\$ – Yatoniea Feb 16 '17 at 23:20
1
\$\begingroup\$

This worked for me:

private bool DoListsMatch(List<int> list1, List<int> list2)
{
    var areListsEqual = true;

    if (list1.Count != list2.Count)
        return false;

    for (var i = 0; i < list1.Count; i++)
    {
        if (list2[i] != list1[i])
        {
            areListsEqual = false;
        }
    }

    return areListsEqual;
}
\$\endgroup\$
  • \$\begingroup\$ But if the number of items in list 1 and list 2 do not match, this will throw an ArrayIndexOutOfBounsException... You should probably add a little check to see if list1.Count == list2.Count. \$\endgroup\$ – JDSweetBeat Feb 11 '17 at 0:37
  • \$\begingroup\$ Thanks for the reply. The method will only be called if there's four items in the craft window so there's no chance of the lists not matching. \$\endgroup\$ – Yatoniea Feb 11 '17 at 0:43
  • \$\begingroup\$ This is a good answer (especially taking the comment into consideration, when it does not necessarily apply to your specific case). There is still the potential for error, if the two lists are not in the exact same order. Feel free to add the Sort lines from my answer to your answer; I felt it was worth posting a separate answer for future users that may be using a custom type, as custom types need to be set up to properly use the Sort method. \$\endgroup\$ – Gnemlock Feb 11 '17 at 3:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.