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.
Answer from Gnemlock on Stack Exchange
unity - Check if two lists are the same - Game Development Stack Exchange
compare two lists regardless of order Unity c# - Stack Overflow
c# - Unity how to compare contents of two arrays regardless of order? - Stack Overflow
How do I compare two lists in C# - Questions & Answers - Unity Discussions
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.
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;
}
Sort first and comprare elements
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class ABCD : MonoBehaviour
{
public void Test()
{
List<int> A = new List<int>() { 1, 3, 5 };
List<int> B = new List<int>() { 5, 3, 1 };
}
bool IsSame(List<int> A, List<int> B)
{
if (A.Count != B.Count)
{
return false;
}
List<int> ASort = A.ToList();
ASort.Sort();
List<int> BSort = B.ToList();
BSort.Sort();
for (int i = 0; i < ASort.Count; i++)
{
if(ASort[i] != BSort[i])
{
return false;
}
}
return true;
}
}
Not sure how it compares to presorting in regards to the performance, but you can use the Contains method of the list to check for elements
bool Equal(List<T> listA, List<T> listB){
bool equal = true;
foreach(var element in listA){
if(!listB.Contains(element)){
equal = false;
break;
}
}
return equal;
}
Note that count of both lists isn't compared as it was assumed that they are equal.
I want to create a list of references to "cell" gameobjects and some of their values; such as metabolismRate(int), individualID(string), speciesID(string), red(float), green(float), blue(float).
The cell gameobject has many more values but I only need to keep a reference to each individual cell (whether that should be a reference to the instance of the gameobject or its script i'm not sure) and some of its values (such as the ones mentioned).
Script looks like this:
public class CellScript : MonoBehaviour {
public int metabolismRate;
public string individualID;
public string speciesID;
public float red;
public float green;
public float blue;
//Many more values...
void Start () {
//1 -- Chance to mutate values
//2 -- Add self to list in ManagerObject...
//...along with certain values for comparison against other individuals
}
void Update () {
//1 -- Chance to instantiate a new instance of itself
}
} I want to keep the list/dictionary inside a manager script. I already have a list of cell gameobjects (inside a manager) which I can use to keep track of the population size of all cells with list.Count but but am having it hard thinking about how to approach comparing the values of individuals inside that list, to do things like:
-
find how many individuals there are with the speciesID of "ay47i"
-
find how many individuals have a metabolism rate of 5 or more
Any anyone could chime in or point me in the right direction i'd really appreciate it -- or let me know if what i'm trying to achieve isn't clear.
You can use linq to query your collections e.g. ListOfThings.Where(t => t.Property == "value").Count() would give you number of things where Property is "value".
Besides querying the list using linq as suggested in the other comment, the other option would be to create callbacks locally on the cellscript. For example, you specified "how many individuals have a metabolism of 5 or more". You could create a callback that when the metabolism rate reaches 5, it increments a local field in your manager or it could add the cellscript object to a list in the manager. If you are consistently checking many variables regarding the cells properties it might be best to query the list/dict for specifically what you need. If your manager is getting too messy with multiple lists, you could set up another class that specifically manages the queries and holds that data.
Using linq:
foreach (var component in rendererComponents.Where(r => r.gameObject.tag != "ignoreRend"))
In addition to Leo Bartkus answer:
Assuming that the items you want to ignore, are also part of your rendererComponents collection, you can just do something like the following:
Just check for the tag in the loop.
foreach (var component in rendererComponents)
{
if (component.tag == "ignoreRend")
{
continue; //this will continue with next item in list
}
component.enabled = true;
}
As Leo suggested you can also use LINQ
In order to use LINQ you have to import it by
using System.LINQ
Then you can use it like:
var filteredComponents = rendererComponents.Where(r => r.gameObject.tag != "ignoreRend")
foreach (var component in filteredComponents)
{
component.enabled = true;
}
For more information on LINQ u may want to visit getting started with LINQ