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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Problems with c#

Discussion in 'Scripting' started by rainbow_design, Feb 14, 2016.

  1. rainbow_design

    rainbow_design

    Joined:
    Aug 8, 2015
    Posts:
    108
    Hello i have a problem with a key:
    I have a dictionary with keys consisting of 2 float numbers but when i create a key with the same numbers i cannot access the dictionary:


    foreach (h.tuf item in mapbuildon.mapcoordinatelist.Keys) {//h.tuf is a key of 2 float values
    if (item.a == coordp.a) {
    if (item.b == coordp.b) {
    Debug.LogWarning (item.a + " found " + item.b);//this finds a result and prints it.
    }
    }
    if (item == coordp) {
    Debug.LogWarning (item.a + " found " + item.b); //this Does not find anything
    }
    }

    Debug.LogError (coordp.a + "mapbuildon " + coordp.b+ mapbuildon.mapcoordinatelist.ContainsKey(new h.tuf (coordp.a, coordp.b))); //returns false even if the key is right
     
  2. notoriousnary

    notoriousnary

    Joined:
    Jul 16, 2015
    Posts:
    9
    Any chance you can attach your full script? , I'm using a Dictionary where some of the keys are duplicated and its working fine :)
     
  3. rainbow_design

    rainbow_design

    Joined:
    Aug 8, 2015
    Posts:
    108
    Hello, the whole script has 700 lines so i copy the concerning classes...
    The Keys used are not the same they have only the same float content
    Code (CSharp):
    1.  
    2.  
    3.         public class tuf {
    4.             public float a { get; private set; }
    5.             public float b { get; private set; }
    6.             internal tuf(float aa, float bb) {
    7.                 a = aa;
    8.                 b = bb;
    9.             }
    10.         }
    11. //creating the map
    12.      public System.Collections.Queue  mapposlista = new System.Collections.Queue ();
    13.      public System.Collections.Queue  mapposlistb = new System.Collections.Queue ();
    14.      public Dictionary<h.tuf,coordobjs>  mapcoordinatelist = new Dictionary<h.tuf,coordobjs> ();
    15.  
    16.        for (int ix = 0; ix < size.a; ix++) {
    17.          for (int iy = 0; iy < size.b; iy++) {
    18.            mapcoordinatelist.Add (new h.tuf (((ix * fieldsize.a) + newmap.transform.localPosition.x - (fieldsize.a * size.a) / 2), ((iy * fieldsize.b ) + newmap.transform.localPosition.y - (fieldsize.b * size.b) / 2)), new coordobjs ());
    19.            mapposlista.Enqueue ((ix * fieldsize.a) + newmap.transform.localPosition.x - (fieldsize.a * size.a) / 2);
    20.            mapposlistb.Enqueue ((iy * fieldsize.b) + newmap.transform.localPosition.y - (fieldsize.b * size.b) / 2);
    21.          }
    22.        }
    23.  
    24.         private h.tuf findrealpos() { //returns the dictionary key posion of the click
    25.             Vector2 localCursor;
    26.             if (!RectTransformUtility.ScreenPointToLocalPointInRectangle (newmap.GetComponent<RectTransform> (), Input.mousePosition, g.maincamera, out localCursor)) {
    27.  
    28.             }
    29.             h.tuf myfl= (new h.tuf (localCursor.x * mapscale.x, localCursor.y * mapscale.y));
    30.             float itema=-1;
    31.             foreach (float item in mapposlista) {
    32.                 if (itema == -1) {
    33.                     if (item > myfl.a - (fieldsize.a / 2)) {
    34.                         itema = item;
    35.                     }
    36.                 }
    37.             }
    38.             float itemb=-1;
    39.             foreach (float item in mapposlistb) {
    40.                 if (itemb == -1) {
    41.                     if (item > myfl.b - (fieldsize.b / 2)) {
    42.                         itemb = item;
    43.                     }
    44.                 }
    45.             }
    46.             Debug.Log ("realpos in" + myfl.a + " + " + myfl.b + " realpos out " + itema + " + " + itemb);
    47.             return new h.tuf (itema, itemb);
    48.         }
     
  4. rainbow_design

    rainbow_design

    Joined:
    Aug 8, 2015
    Posts:
    108
    It boils down to this how can i get this one right, i dont get the output since (new h.tuf (3, 3) == new h.tuf (3, 3)) seems to be false:

    Code (CSharp):
    1.  
    2.  
    3.      public class tuf {
    4.        public float a { get; private set; }
    5.        public float b { get; private set; }
    6.        internal tuf(float aa, float bb) {
    7.          a = aa;
    8.          b = bb;
    9.        }
    10.      }
    11.         bool tester;
    12.         if (new h.tuf (3, 3) == new h.tuf (3, 3)) {//this seems to return false
    13.             tester = true;
    14.             Debug.LogWarning (" compare " + tester);//this finds a result and prints it.
    15.         };
     
  5. grizzly

    grizzly

    Joined:
    Dec 5, 2012
    Posts:
    356
    Tuf is a class. A class is a reference type. You're comparing two different references not values. If this is not intended, try adding an Equals method to your Tuf class;

    Code (CSharp):
    1. public bool Equals (tuf other) {
    2.  
    3.     return Mathf.Approximately(this.a, other.a) && Mathf.Approximately(this.b, other.b);
    4. }
    Mathf.Approximately is used here since we're comparing floats. Floats can give false positives due to precision.
     
    notoriousnary likes this.
  6. notoriousnary

    notoriousnary

    Joined:
    Jul 16, 2015
    Posts:
    9
    Not quite sure why if(new h.tuf(3, 3)==new h.tuf(3, 3)) is returning false, Assuming your just trying to check if the a or b components are the same


    Code (CSharp):
    1. bool Tester()
    2.     {
    3.         help.tuf tuf1 = new tuf(3, 3);
    4.         help.tuf tuf2 = new tuf(3, 3);
    5.  
    6.      
    7.  
    8.         if (tuf1.a == tuf2.a && tuf1.b == tuf2.b)
    9.         {
    10.            
    11.             return true;
    12.         }
    13.         else
    14.         {
    15.             return false;
    16.         }
    17.     }
    Works fine, or you can also add the following to the Tuf class.

    Code (CSharp):
    1.         public bool TufCompare(tuf obj)
    2.         {
    3.             if(obj != null || GetType() == obj.GetType())
    4.             {
    5.                 if(obj.a == a && obj.b == b)
    6.                 {
    7.                     return true;
    8.                 }
    9.                 else
    10.                 {
    11.                     return false;
    12.                 }
    13.             }
    14.             else
    15.             {
    16.                 return false;
    17.             }
    18.         }
    then you can simply call it as below
    Code (CSharp):
    1.         bool compare = new tuf(3, 3).TufCompare(new tuf(3, 3));
    2.        
    3.         Debug.LogWarning(" compare " + compare);//this finds a result and prints it.
     
  7. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    Classes are reference types.
    Code (CSharp):
    1. if (new h.tuf (3, 3) == new h.tuf (3, 3))
    This doesn't work as it compares references, not each of the classes attributes one by one. You have to explicitly define that by either overriding that operator or even better, overriding Equals() and GetHashCode().
     
  8. notoriousnary

    notoriousnary

    Joined:
    Jul 16, 2015
    Posts:
    9
    Ah completely forgot about overriding Equals I'd go with this solution ^
     
  9. rainbow_design

    rainbow_design

    Joined:
    Aug 8, 2015
    Posts:
    108
    Thankyou but the problem is not to compare the variables but to use tuf as a key in

    Code (CSharp):
    1. Debug.LogError (coordp.a + "mapbuildon " + coordp.b+ mapbuildon.mapcoordinatelist.ContainsKey(new h.tuf (coordp.a, coordp.b))); //returns false even if the key is right
    2.  
    That is how my problem started.... mapcoordinatelist has tuf as a key and i wonder how to access it when the key is lost but i have the variables from the key.
    or otherwise how pack a coordinatepair so i can acess it this way.
     
  10. grizzly

    grizzly

    Joined:
    Dec 5, 2012
    Posts:
    356
    When you write "new" you're creating a new instance with a reference different to those in the dictionary. You need to store a reference to the object you're looking for and use that instead.
     
  11. grizzly

    grizzly

    Joined:
    Dec 5, 2012
    Posts:
    356
    Sorry, missed this...

    You could use a nested dictionary;
    Code (CSharp):
    1. Dictionary<float, Dictionary<float, object>> coordinates = new Dictionary<float, Dictionary<float, object>>();
    Using known coordinates, you can easily retrieve the object like so;
    Code (CSharp):
    1. object obj = coordinates[coordA][coordB];
     
  12. rainbow_design

    rainbow_design

    Joined:
    Aug 8, 2015
    Posts:
    108
    I did this now it seems to work:

    Code (CSharp):
    1.  
    2.             foreach (h.tuf key in mapcoordinatelist.Keys) {
    3.                 if (key.a == itema) {
    4.                     if (key.b == itemb) {
    5.                         Debug.Log ("realpos in" + myfl.a + " + " + myfl.b + " realpos out " + itema + " + " + itemb);
    6.                         return key;
    7.                     }
    8.                 }
    9.             }
    Thankyou everyone.
     
  13. grizzly

    grizzly

    Joined:
    Dec 5, 2012
    Posts:
    356
    Excellent, albeit slower, that's certainly another way! However you'll still need to use the Mathf.Approximately method shown above to ensure that the coordinates are compared correctly - which isn't always the case with floating point values. :)
    Code (CSharp):
    1. foreach (h.tuf key in mapcoordinatelist.Keys) {
    2.          if (Mathf.Approximately(key.a, itema)) {
    3.            if (Mathf.Approximately(key.b, itemb)) {
    4.              Debug.Log("realpos in" + myfl.a + " + " + myfl.b + " realpos out " + itema + " + " + itemb);
    5.              return key;
    6.            }
    7.          }
    8.        }
    9.  
     
  14. rainbow_design

    rainbow_design

    Joined:
    Aug 8, 2015
    Posts:
    108
    that should be in the function already thiss ist the full function it seems to give the right values:

    Code (CSharp):
    1.         private h.tuf findrealpos() { //returns the dictionary key posion of the click
    2.             Vector2 localCursor;
    3.             if (!RectTransformUtility.ScreenPointToLocalPointInRectangle (newmap.GetComponent<RectTransform> (), Input.mousePosition, g.maincamera, out localCursor)) {
    4.  
    5.             }
    6.             h.tuf myfl= (new h.tuf (localCursor.x * mapscale.x, localCursor.y * mapscale.y));
    7.             float itema=-1;
    8.             foreach (float item in mapposlista) {
    9.                 if (itema == -1) {
    10.                     if (item > myfl.a - (fieldsize.a / 2)) {
    11.                         itema = item;
    12.                     }
    13.                 }
    14.             }
    15.             float itemb=-1;
    16.             foreach (float item in mapposlistb) {
    17.                 if (itemb == -1) {
    18.                     if (item > myfl.b - (fieldsize.b / 2)) {
    19.                         itemb = item;
    20.                     }
    21.                 }
    22.             }
    23.             foreach (h.tuf key in mapcoordinatelist.Keys) {
    24.                 if (key.a == itema) {
    25.                     if (key.b == itemb) {
    26.                         Debug.Log ("realpos in" + myfl.a + " + " + myfl.b + " realpos out " + itema + " + " + itemb);
    27.                         return key;
    28.                     }
    29.                 }
    30.             }
    31.             Debug.LogError ("key not found in findrealpos"+ myfl.a+ myfl.b);
    32.             return myfl;
    33.         }
     
  15. rainbow_design

    rainbow_design

    Joined:
    Aug 8, 2015
    Posts:
    108

    I still feel my solution is messy its troubling me i want to change it to something i can access easy with a key of 2 numbers but iterate over it too

    right now this works for me:

    foreach (coordobjs item in mapcoordinatelist.Values) {

    a dictionary would need two loops as example which also means i would need to change quite some of my code to something more complicated also i have many occurances where i just pass the single key if i use a dictionary i need to pass 2 keys which again means i have to change quite a bit of functions.
    any suggestion for a single key that would work?

    Right now the class i use looks like this:

    Code (CSharp):
    1.  
    2.         public class tuf {
    3.             public float a { get; private set; }
    4.             public float b { get; private set; }
    5.             internal tuf(float aa, float bb) {
    6.                 a = aa;
    7.                 b = bb;
    8.             }
    9.         }
    10.  
    11.  
    and then:
    mapcoordinatelist.Add (new h.tuf (((ix * fieldsize.a) + newmap.transform.localPosition.x - (fieldsize.a * size.a) / 2), ((iy * fieldsize.b ) + newmap.transform.localPosition.y - (fieldsize.b * size.b) / 2)), new coordobjs ());
     
    Last edited: Feb 29, 2016
  16. bdev

    bdev

    Joined:
    Jan 4, 2011
    Posts:
    656
    you likely want to do the following:
    Code (csharp):
    1.  
    2. private sealed class tufequality : System.Collections.Generic.EqualityComparer<h.tuf> {
    3.  public override int GetHashCode(h.tuf x) { return x.a.GetHashCode() ^ ( x.b.GetHashCode() << 3 ); }
    4.  public override bool Equals(h.tuf x, h.tuf y) { return x.a.Equals(y.a) && x.b.Equals(y.b); }
    5.  private tufequality(){}
    6.  public static readonly System.Collections.Generic.IEqualityComparer<h.tuf> instance = new tufequality();
    7. }
    8.  
    then when you make your dictionary..
    Code (csharp):
    1.  
    2.      public Dictionary<h.tuf,coordobjs>  mapcoordinatelist = new Dictionary<h.tuf,coordobjs> (tufequality.instance);
    3.  
    that will give you a working dictionary where h.tuf will be the key and the equality comparer will be in charge of checking if an item already exists or not in the dictionary.

    but i do suggest you change h.tuf to a struct, implement System.IEquatable<h.tuf> and override GetHashCode, Equals, ToString then add == and != operators. You did good by keeping a and b unmutable, but structs should be a bit better and wont require the property usage at all (since they are immutable while serving as a key in the dictionary).
    Also, others suggested using Mathf.Approximately. If you want this to be used as a key in a dictionary, do not use Mathf.Approximately. The HashCode of the key is significant and Mathf.Approximately is in most cases just going to be comparing the exact same float values because of that.

    lastly, instead of doing x.a == y.a ect. consider using x.a.Equals(y.a). The difference is that float.NaN will never equal float.NaN because they are both not numbers when you use the inline == operator, where Equals will return true.
     
    rainbow_design likes this.
  17. rainbow_design

    rainbow_design

    Joined:
    Aug 8, 2015
    Posts:
    108
    Thankyou i did this now:
    public struct tuf {
    and followed x.a == y.a ect. consider using x.a.Equals(y.a) (the one of the build in function.

    now
    if (new h.tuf (3, 3).Equals (new h.tuf (3, 3)))

    returns true :)
    so thats what i actually wanted. And for that i give to you the Helper award of the day ;) Thankyou.
     
  18. bdev

    bdev

    Joined:
    Jan 4, 2011
    Posts:
    656
    Good stuff, yea generally whenever i'm making something that is to act as a key of sort ( or really, any kind of struct ) I always make the extra effort to override all of the above (including ToString to avoid garbage allocation) and at minimum the == and != operators. The moment you do this you end up with a type that immediately fits hundreds of different collection types, so its usually worth the extra effort.