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

Resolved How to sort array by name/string?

Discussion in 'Scripting' started by doodler345, Oct 4, 2022.

  1. doodler345

    doodler345

    Joined:
    Dec 30, 2021
    Posts:
    27
    Hey Guys,

    I use this:

    allWaypointsGameObjects = GameObject.FindGameObjectsWithTag("BotWaypoint");

    Unfortunately the order of elements can vary here... So when I make a WebGL-Build, the order is not like in the Editor.
    So how can I sort all arrays by name?
    Meaning Waypoint1, Waypoint2...

    Thanks alot!
     
  2. Homicide

    Homicide

    Joined:
    Oct 11, 2012
    Posts:
    656
    You can get the proper order, as seen in editor , with a little mucking around.

    First, locate the transform in the scene that contains all the waypoints (im assuming for logical and clarity reasons, all the waypoints would be under one root transform gameObject somehwere. GetComponentsInChildren of the root transform... should return them in order as placed, unlike FindObject.

    I use this approach often and it hasnt let me down yet.
     
    doodler345 likes this.
  3. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,951
    You can sort almost any array by almost anything!

    In your case above you could use:

    Code (csharp):
    1. System.Array.Sort( allWaypointsGameObjects,
    2.              (a,b) => { return a.name.CompareTo( b.name); });
     
    Bunny83, doodler345 and Homicide like this.
  4. Homicide

    Homicide

    Joined:
    Oct 11, 2012
    Posts:
    656

    Or like that, though i find alot of newer folks trip on the expression for awhile.
     
  5. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,951
    "Whatcha doin' bro?"

    "Aw man, just trippin' on this lambda baby."

    "Righteous!"

    To OP and any others trippin' on lambdas (one of my favorite pasttimes actually), this code:

    Code (csharp):
    1. (a,b) => { return a.name.CompareTo( b.name); }
    is a lambda, or transient nameless function.

    It takes two GameObjects
    a
    and
    b
    (inferred from the declaration of Sort()) and returns the result of comparing their
    .name
    fields.

    Now we can all trip out on lambdas together!

    "Don't bogart the lambda, man..."
     
    Homicide likes this.
  6. doodler345

    doodler345

    Joined:
    Dec 30, 2021
    Posts:
    27
    I'll understand in future
    Thank you alot, I'll research for "Lamda"
     
  7. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    until you do, you may do things the old-fashioned way, if you feel like that is more in line with your understanding.

    the Sort method on lists and arrays relies on a so-called delegate declaration called
    Comparison

    it declares the following
    Code (csharp):
    1. public delegate int Comparison<in T>(T x, T y);
    so what this says is basically you would need to supply a function that has the same signature
    you obviously replace T with string for example and you get
    Code (csharp):
    1. int MyComparison(string a, string b) {
    2.   // something
    3. }
    That something is shown by Kurt above
    Code (csharp):
    1. a.name.CompareTo(b.name);
    because strings implement IComparable<string> which requires CompareTo method
    so you can also do this, and ignore lambdas
    Code (csharp):
    1. myList.Sort(MyComparison); // < obviously this belongs inside some function
    2.  
    3. int MyComparison(string a, string b) {
    4.   return a.CompareTo(b);
    5. }
    That's all
     
    doodler345 and Kurt-Dekker like this.
  8. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,571
    I really like that you also try to be as specific as possible in the hope that future readers get the most out of the answer.

    The fact that lambda expressions are usually used with inferred arguments makes it often harder to read as the type is not necessarily immediately obvious in all cases. Note that you can actually specify the type of the arguments of a lambda expression explicitly:
    (GameObject a, GameObject b) => ...


    Another little quirk about lambdas is, when you only have a single argument (not the case for Sort, but you may encounter it elsewhere), the parentheses can be omitted and it's just
    a=>Dosomething(a, 42);
    . It's also possible to use the lambda syntax for methods that don't receive any arguments like that:
    ()=>...
    .

    Since we're already beating a dead horse, lets continue :)

    A lambda expression is usually meant to return a value. The return type is also inferred from whatever the
    =>
    points to. So
    ()=>"Hello World";
    is a method without arguments that returns a string.

    It's also possible to use the lambda syntax with a full method body by using curly braces. In that case, if you want to return a value, you have to use the usual
    return
    keyword.

    Code (CSharp):
    1. System.Func<int> someFunc = ()=>{Debug.Log("I was here"); return 42;};
    2. int val = someFunc(); // returns 42 and prints "I was here"
    That's pretty much all that could be said about lambdas I guess ^^

    edit:
    I just forgot to mention that lambda expressions, just like ordinary anonymous methods can also become "closures". That means they can actually get hold of a local variable outside the method scope. Note that closures actually "close over" / capture the actual variable, not just the value it contains. To illustrate that, here's a total crazy construct:
    Code (CSharp):
    1.     public struct Crazy
    2.     {
    3.         public System.Func<int> myGetter;
    4.         public System.Action<int> mySetter;
    5.         public System.Action someAction;
    6.         public static Crazy Create()
    7.         {
    8.             int someLocalVariable = 42;
    9.             Crazy ret = new Crazy();
    10.             ret.myGetter = ()=>someLocalVariable;
    11.             ret.mySetter = a=>someLocalVariable = a;
    12.             ret.someAction = ()=>someLocalVariable++;
    13.             return ret;
    14.         }
    15.     }
    16.  
    With that struct we can do this:

    Code (CSharp):
    1. var s = Crazy.Create();
    2. Debug.Log("val: " + s.myGetter()); // prints "val: 42";
    3. s.mySetter(7);
    4. Debug.Log("val: " + s.myGetter()); // prints "val: 7";
    5. s.someAction();
    6. Debug.Log("val: " + s.myGetter()); // prints "val: 8";
    7.  
    Note that the struct itself does not contain any int variable, only 3 function pointers / delegates. Those 3 lambda expressions are all closures since they use a variable from outside their scope. Usually that local variable would only exist while the "Create" method is still running. However a closure will actually encapsulate the variable in a closure object that outlives the Create method.

    Closures can be useful in a lot cases, especially for sorting or search function. For example if you want to sort a gameobject array based on their distance from another object, the lambda can actually capture that other object and use it inside the comparison function.
     
    Last edited: Oct 4, 2022
  9. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    I was crazy enough to think you've omitted to mention capturing, but then you went overboard lol xD
     
    Bunny83 likes this.
  10. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    Though you did forget to say a thing or two about the generic delegates Action and Func. But it's hard to talk about it, if readers have no context about delegates in general.

    Whoever is reading this, maybe you'll find this bit useful:
    Just like classes declare and typify objects and interfaces prescribe public portions of these objects, delegates declare the method signatures to be used as a template for some local implementation. The important part is that delegates are top-level declarations just as class and interfaces are!

    (Unless you nest such declarations, then they're nested, which is usually how delegates are used, but there are cases where this isn't desirable.)

    I'm sure there are people who're like "duh" but for some reason I never thought of them as top-level, maybe because they're method-like, idk. It took me some time to confirm this, sadly I forgot the exact situation that prompted me to look it up, but it was totally some kind of chicken-egg problem.
     
    Bunny83 likes this.
  11. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,951
    That's actually not a bad practice, kinda like named arguments. But it gets a little wordy.

    Honestly in most of my code I air my lambda's out vertically unless they really are trivial.

    Such as this monstrosity in Jetpack Kurt:

    Code (csharp):
    1. DamagePropertyImpactSensor.Attach(
    2.             rb,
    3.             (deadlinessType) => {
    4.              
    5.                 if (!ready) return;
    6.  
    7.                 if (YouHaveDied) return;
    8.  
    9.                 TrackAppliedDamage(100);
    10.  
    11.                 MissionLogEntryCollection.Add( MissionLogType.SHIP_DAMAGED, "Deadly contact");
    12.  
    13.                 YouAreDead();
    14.  
    15.                 switch( deadlinessType)
    16.                 {
    17.                 default :
    18.                     break;
    19.  
    20.                 case DeadlinessType.SHAKING :
    21.                     PlayerType1DemiseShaking.Attach( rb.gameObject, alsoGameOver: false);
    22.                     break;
    23.  
    24.                 case DeadlinessType.ELECTROCUTION :
    25.                     PlayerType1DemiseElectrocution.Attach( rb.gameObject, alsoGameOver: false);
    26.                     PlayerType1DemiseShaking.Attach( rb.gameObject, alsoGameOver: false);
    27.                     break;
    28.                 }
    29.             },
    30.             () => {
    31.                 return true;
    32.             }
    33.         );
    I really should name those last two arguments but perhaps I'm just beating a dead lambda.

    At least the lambdas don't have lambdas inside 'em. That's really when I reach for good old functions!

    But I guess it has a few .Attach() calls within .Attach()... I almost never AddComponent<T>() randomly in code... it always seems worthwhile to wrap it in a static factory method called .Attach() if you already HAVE a GameObject, or .Create() if you want one created. Part of this pattern:

    Factory Pattern in lieu of AddComponent (for timing and dependency correctness):

    https://gist.github.com/kurtdekker/5dbd1d30890c3905adddf4e7ba2b8580
     
    orionsyndrome and Bunny83 like this.