Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Contains on a list of arrays

Discussion in 'Scripting' started by andrescuevas, Jun 10, 2019.

  1. andrescuevas

    andrescuevas

    Joined:
    Apr 23, 2019
    Posts:
    24
    I want to make a list of string ,value, value pairs for the children of an object. The values are taken from a script in the children. So I do this:

    Code (CSharp):
    1.  
    2. List<string[]> cosas = new List<string[]>();
    3. public int sameItemCounter = 0;
    4.  
    5. public void AddList()
    6.     {
    7.         string[] g = new string[2];
    8.        var itemStats = transform.GetChild(transform.childCount - 1).GetComponent<Stats>();
    9.         g[0] = itemStats.cosa;
    10.         g[1] = itemStats.colorTag;
    11.         cosas.Add(g);
    12.  
    13.     }
    an example of the value, value pair would be: "pig", "red".

    I then want to make a check to see if there are all of the colors of the same item in the list. for example if pig red, pig blue, pig green, pig yellow are all there.

    Code (CSharp):
    1. public void CheckSameItem()
    2.     {
    3.         for (int i = 0; i < cosas.Count; i++)
    4.         {
    5.             var b = new string[2];
    6.             b[0] = cosas[i][0];
    7.             b[1] = "Blue";
    8.  
    9.             var r = new string[2];
    10.             r[0] = cosas[i][0];
    11.             r[1] = "Red";
    12.  
    13.             var g = new string[2];
    14.             g[0] = cosas[i][0];
    15.             g[1] = "Green";
    16.  
    17.             var y = new string[2];
    18.             y[0] = cosas[i][0];
    19.             y[1] = "Yellow";
    20.  
    21.             if (cosas.Contains(r) && cosas.Contains(b) && cosas.Contains(g) && cosas.Contains(y))
    22.             {
    23.                 sameItemCounter++;
    24.                 print("is ok");
    25.             }
    26.  
    27.     }
    I make sure the right children are there but the condition is never met. How can I make this work?
     
  2. palex-nx

    palex-nx

    Joined:
    Jul 23, 2018
    Posts:
    1,745
    The Contains method use Object.Equals method on list items to determine if list contains the item specified as it's argument.

    This
    Code (CSharp):
    1. var list = new List<string[]>();
    2. var item = { "abc", "def" };
    3. list.Add(item);
    4. Debug.Log(list.Contains(item));
    will print True. And this

    Code (CSharp):
    1. var list = new List<string[]>();
    2. var item = new string[] { "abc", "def" };
    3. list.Add(item);
    4. Debug.Log(list.Contains(new string[] { "abc", "def" }));
    will print False because Array.Equals is not overriden and defaults to Object.ReferenceEquals. By default all objects includig arrays are compared by reference, not by content.
     
  3. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    I would argue using a List<string[]> is a poor option for storing a list of key value pairs since a string[] does not constrain how many entries it can have. You can easily put an array of length 1, 3, 9, N into the list. Furthermore you're creating new objects every time since arrays are ref types.

    There is already a struct that acts as a key value pair. It's called KeyValuePair<TKey, TValue> of all things:
    https://docs.microsoft.com/en-us/do....generic.keyvaluepair-2?view=netframework-4.8

    And the equality isn't by ref since it's a struct. Instead it compares by if the key and value match.

    ...

    Mind you, I'm not sure what it is your List of key value pairs is for... but um, if it's for looking up a value matched with its key. You probably want a Dictionary... which uses KeyValuePair as its members.
    https://docs.microsoft.com/en-us/do...eneric.dictionary-2.add?view=netframework-4.8
     
    SparrowGS likes this.
  4. andrescuevas

    andrescuevas

    Joined:
    Apr 23, 2019
    Posts:
    24
    ok I think I understand, but how could I compare by content? or this is not possible?
     
  5. andrescuevas

    andrescuevas

    Joined:
    Apr 23, 2019
    Posts:
    24
    I think I can´t use key value pair or dictionary because my items don´t have unique keys. or could i have entries like this:
    [pig, red]
    [pig, blue]
    etc...
    ?
    pig as a key would have to be unique no?

    thats why I need a VALUE VALUE pair if such a thing exists...
     
  6. palex-nx

    palex-nx

    Joined:
    Jul 23, 2018
    Posts:
    1,745
    To compare by content you have two options. You may use your own class to store two fields like a class Pair {public string a; public string b; } and override Equals method for that class, following this guide: https://www.c-sharpcorner.com/Uploa...c-sharp-basics-why-we-override-equals-method/

    Or, you may use List.Find or List.FindIndex methods which allows you to specifiy comparsion function as lambda expression.

    Both options are not the best choice. First includes about one hour of working (reading the article + writing the code + debugging the code) and a lot of headache later, when number of items will grow or additional fields will be required. Second is not so good because labdas often lead to hidden allocations, wich isn't good for performance. Single lambda won't create the problem. But lambdas themselves are sexy, use one and you want to use it again and again everywhere and this will create a lot of problems with garbage collection and memory fragmentation later. I would follow lordofduct's advice and ajust your data structure to fit default collections.
     
  7. andrescuevas

    andrescuevas

    Joined:
    Apr 23, 2019
    Posts:
    24
    ok, i will try to change data structure. tyvm!
     
  8. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    You can't use Dictionary.

    But you can use List<KeyValuePair<...>>.

    KeyValuePair does not require uniqueness of keys. The Dictionary is what enforces that.
     
  9. andrescuevas

    andrescuevas

    Joined:
    Apr 23, 2019
    Posts:
    24
    OK, I will give it a try. Thank you!
     
  10. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    Furthermore, if you don't like KeyValuePair... or you want somethign that serializes with the unity serializer (since it doesn't support generics).

    Just create your own struct:

    Code (csharp):
    1.  
    2. [System.Serializable]
    3. public struct MyKeyValuePair
    4. {
    5.     public string Key;
    6.     public string Value;
    7. }
    8.  
    Done.
     
    Joe-Censored likes this.
  11. SparrowGS

    SparrowGS

    Joined:
    Apr 6, 2017
    Posts:
    2,536
    @lordofduct Maybe I don't understand what it is he's trying to do, but my answer was a dictionary, I don't see why this wouldn't be a an option.

    to my understanding, the OP needs to have animals(?, pig, duck, etc), each in unique colors.

    if so - why wouldn't this do?
    Code (CSharp):
    1. public class MyExample {
    2.  
    3. Dictionary<string, List<string>>myDict;
    4.  
    5. void AddToDictionary(string animal, string color){
    6.  
    7. if(myDict.ContainsKey(animal){
    8.    if(myDict[animal].Contains(color))
    9.       //color already in animals list
    10.    else
    11.       myDict[animal].Add(color);
    12. } else { //No entries for animal yet
    13.    myDict.Add(animal, new List<string>());
    14.    myDict[animal].Add(color);
    15. }
    16.  
    17. }
    18.  
    19. }
    If it was my solution I'd probably avoid all the strings and use an int for the animal and an actual Color for the, um.. well, color.
     
  12. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    I agree, I originally thought dictionary as well.
    But OP came back saying that they can't because the keys aren't necessarily unique:
    I don't know why pig would have 2 distinct colours... but that's what OP said are their requirements. So Dictionary it is not... but a List<KeyValuePair<...>> is valid (or several other collections including Array, HashSet, SortedList, and more).
     
    Last edited: Jun 11, 2019
  13. SparrowGS

    SparrowGS

    Joined:
    Apr 6, 2017
    Posts:
    2,536
    He maybe mistaken, maybe he thinks he needs to store each color in a new entry associated with the animal?
    IE "myDict.Add(animal, color);" instead of "myDict.Add(animal, new List<string>());" and adding colors to the list.
    (OP, in my solution you hold one list of colors for each 'animal')

    if he meant that you can have any numbers of pigs each with it's own colors then yeah, i'd advise to use a custom struct too as you suggested.
     
  14. halley

    halley

    Joined:
    Aug 26, 2013
    Posts:
    1,873
    Don't create a class from scratch just to get around generics. Derive a new class from the.generic you want.

    public class MyThing: TheirGenericThing<T> { }


    Done.
     
  15. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    That could be so. Will have to wait for OP to confirm which way or the other.

    You can't inherit from a struct.

    And in the use case OP is talking about... a struct is the preferred type since 2 keyvaluepairs of the same values should be equal. Hence OP's initial issue of 2 arrays, despite having the same length and entries, weren't comparing equal since Array is a ref type, rather than a value type (struct).
     
    SparrowGS likes this.
  16. halley

    halley

    Joined:
    Aug 26, 2013
    Posts:
    1,873
    Fair point. Ridiculous language quirk, but fair point.
     
  17. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    It actually makes sense if you look at what a struct is from a memory perspective.

    A struct is not referenced, it is copied by value. This means if you for instance call a method and pass in a struct, the memory required by the struct needs to be allocated on the stack along with the method. This also means we need to know exactly how big the struct is to allocate the appropriate amount of memory for the struct.

    That amount of memory is the total of all memory required for each field of the struct (plus any padding that may occur).

    Now... lets introduce inheritance.

    What if you have a pair of structs like our Vector2 and Vector3, and instead of being distinct, we inherited:
    Code (csharp):
    1.  
    2. public struct Vector2
    3. {
    4.     public float x;
    5.     public float y;
    6. }
    7.  
    8. public struct Vector3 : Vector2
    9. {
    10.     public float z;
    11. }
    12.  
    In theory you'd think this would work. But now what if we have the function:
    Code (csharp):
    1.  
    2. public static void Foo(Vector2 v)
    3. {
    4.     //do stuff
    5. }
    6.  
    But we called it with a Vector3:
    Code (csharp):
    1. Foo(new Vector3(1,2,3));
    What gets allocated on the stack? The compiler at compile time doesn't know what is being passed in, so it has to use a Vector2. But if you later then pass in the Vector3, that means only enough memory is allocated for the x,y... you lose the z (or worse, if the runtime doens't do a safety check, it overflows the stack... this safety check causing overhead).

    Now you might say... whatevs, if it's a struct, it's a struct. You lose that information. So thusly you can't cast the Vector2 to a Vector3 and retrieve that information. But then you lose the power of inheritance... so what's the point OF inheritance in this case? You have "half inheritance"? You can treat it like the sub type, but you lose all information related to it?

    You could also argue that it creates overloaded versions for all sub types and the JIT compiler creates the necessary one on the fly. Basically how 'generics' works. But struct predates generics and runtime overloading by several years (since it's been with C# since the original version). And that's also really wonky and smelly as well.

    ...

    Now you might say in a language like C++ this isn't a problem. Classes and Structs can inherit willy nilly all the same. So why couldn't C# do it the same way?

    The thing is in C++ both class/struct are effectively treated the same (they're basically identical, main difference being the default access modifier of its members). To reference a struct/class in C++ you create pointers and pass those around instead. And a pointer is really just an int value that is the address of the class/struct instance in memory. So really you're just passing ints around when you pass pointers into a method. Not the same thing as C# at all.
     
    DonLoquacious and SparrowGS like this.
  18. andrescuevas

    andrescuevas

    Joined:
    Apr 23, 2019
    Posts:
    24
    Hello, sorry for the delay, I hadn't seen there were replies. In my game you can "steal" many things. I wanted the collection of stolen items, in order to check whether the player had stolen at least one item of each color in order to give her a bonus. The problem with dictionary as I remember was that the player could easily have many of the same item ie. "redpigs" in her inventory, so there was no unique key. Anyway I found a way around all of this. It wasn´t interesting or anything, I´m just a noob finding out about things. Thank you everyone! I´m very surprised and grateful at how people help each other out.