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

Sort String array with repeated items

Discussion in 'Scripting' started by mohsenz, Feb 8, 2022.

  1. mohsenz

    mohsenz

    Joined:
    Aug 10, 2016
    Posts:
    29
    How can I sort:
    (d,c,d,c,a,c,d,a,b,d)
    To
    (d,c,a,b)
    ?
     
  2. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    14,446
    mohsenz likes this.
  3. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,722
    Are you asking how to sort? Or are you asking how to remove duplicates? Kinda seems like you want to remove duplicates and preserve the original ordering. C# gives us this really easy way:

    Code (CSharp):
    1. using System.Linq; // < this needs to be at the top of your file
    2.  
    3. List<Something> myList = // whatever
    4. myList = myList.Distinct().ToList();
     
    mohsenz, Bunny83 and JeffDUnity3D like this.
  4. RadRedPanda

    RadRedPanda

    Joined:
    May 9, 2018
    Posts:
    1,593
    Just sort it normally, but when you compare and there's a duplicate, just delete it.
     
  5. mohsenz

    mohsenz

    Joined:
    Aug 10, 2016
    Posts:
    29
    Thanks
    I mean, sort by (the most duplicated items)
    Then preferably remove the duplicates.
     
  6. mohsenz

    mohsenz

    Joined:
    Aug 10, 2016
    Posts:
    29
    Then if i reach that goal
    I'll improve it to smthing like

    (d 4 times
    c 3 times
    a 2 times
    b 1 time)
     
    Last edited: Feb 8, 2022
    JeffDUnity3D likes this.
  7. AnimalMan

    AnimalMan

    Joined:
    Apr 1, 2018
    Posts:
    1,164
    Create 4 Variable List A, B, C and D

    for iterate array(the unsorted array); add to corresponding list you just var’d
    A adds A
    B adds B
    C adds C
    D adds D

    Combine lists in whichever order you want at the end. AddRange. And recast it back into an array (if you still need array)

    Requires usage of for loop,
    Requires creation of 4 empty lists
    Requires population of these lists
    Requires combination of these lists
    Outputs ; user designated sort order
     
    mohsenz likes this.
  8. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,524
    Well, the easiest way to first produce the duplicate count for each unique element would be to use a
    Dictionary<E, int>
    Where E is your element type. It's not clear if we talk about strings here or something else. Anyways you would simply iterate through your array and increment the counter in the dictionary for each unique element.

    As a result you have a dictionary with only the unique elements paired with their occurrence count. Finally you can create a list our of the key value pairs of the dictionary and sort it based on the value.

    Code (CSharp):
    1. public static List<T> SortByUniqueCount<T>(List<T> aElements)
    2. {
    3.     var dict = new Dictionary<T, int>();
    4.     foreach(T val in aElements)
    5.     {
    6.         int count;
    7.         if (!dict.TryGetValue(val, out count))
    8.             count = 0;
    9.         count++;
    10.         dict[val] = count;
    11.     }
    12.     return dict.OrderByDescending(e => e.Value).Select(e => e.Key).ToList();
    13. }
    This method should return the desired result. It requires the namespaces
    System.Collections.Generic
    as well as
    System.Linq
    .
     
    mohsenz, Olmi, orionsyndrome and 2 others like this.
  9. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    Edit4:
    Oh, I just figured the OP wants to sort specifically by count of occurrence!
    Than bunny83's is the most complete answer. Sorry about that!
    --

    A slightly different solution, without dictionary or Linq, which I'd consider a base line for this case.


    Extensions
    Code (csharp):
    1. public static T[] GetUniqueItemArray<T>(this IList<T> list) {
    2.   var hset = new HashSet<T>();
    3.   for(int i = 0; i < list.Count; i++) hset.Add(list[i]);
    4.   var ary = new T[hset.Count];
    5.   hset.CopyTo(ary);
    6.   return ary;
    7. }
    Code (csharp):
    1. public static List<T> GetUniqueItemList<T>(this IList<T> list) {
    2.   var hset = new HashSet<T>();
    3.   var res = new List<T>();
    4.   for(int i = 0; i < list.Count; i++) {
    5.     if(!hset.Contains(list[i])) res.Add(list[i]);
    6.     hset.Add(list[i]);
    7.   }
    8.   return res;
    9. }
    Code (csharp):
    1. public static List<T> GetUniqueSorted<T>(this IList<T> list, Comparison<T> comparison = null) where T : IComparable<T> {
    2.   comparison??= (a,b) => a.CompareTo(b);
    3.   var unique = list.GetUniqueItemList();
    4.   unique.Sort(comparison);
    5.   return unique;
    6. }
    Code (csharp):
    1. public static string ToCSV<T>(this IList<T> list) {
    2.   var sb = new System.Text.StringBuilder();
    3.   for(int i = 0; i < list.Count; i++) {
    4.     if(i > 0) sb.Append(", ");
    5.     sb.Append(list[i].ToString());
    6.   }
    7.   return sb.ToString();
    8. }

    Usage example
    Code (csharp):
    1. var x = new string[] { "d", "c", "d", "c", "a", "c", "d", "a", "b", "d" };
    2.  
    3. var ascSort = x.GetUniqueSorted();
    4. Debug.Log($"Ascending: {ascSort.ToCSV()}"); // a, b, c, d
    5.  
    6. var descSort = x.GetUniqueSorted( (a,b) => b.CompareTo(a) );
    7. Debug.Log($"Descending: {descSort.ToCSV()}"); // d, c, b, a

    Some notes:
    IList<T>
    interface works with arrays as well.
    Similar to
    Dictionary<K, V>
    ,
    HashSet<T>
    relies on good hashes for performance.
    Also lives in
    System.Collections.Generic
    .
    IComparable<T>
    is optional, but speeds up things considerably because it lets you avoid boxing/unboxing.
    All primitives are already
    IComparable
    .

    Edit:
    Alternatively, GetUniqueSorted can be made cheaper, where each added element would undergo BubbleSort, which is more performant for lists where unique items are expected to be fewer than, say 100. I have a similar solution in my libraries, but obviously it gets a bit more complicated, so let's not drown in minutiae.

    Edit2:
    Changed names a little.

    Edit3:
    As a convenience while I'm at it
    Code (csharp):
    1. public static string ToCSV<T>(this HashSet<T> hset) {
    2.   var sb = new System.Text.StringBuilder();
    3.   int i = 0;
    4.   foreach(var k in hset) {
    5.     if(i++ > 0) sb.Append(", ");
    6.     sb.Append(k.ToString());
    7.   }
    8.   return sb.ToString();
    9. }
    10.  
    11. public static string ToCSV<TKey, TValue>(this IDictionary<TKey, TValue> dict) {
    12.   var sb = new System.Text.StringBuilder();
    13.   int i = 0;
    14.   foreach(var kv in dict) {
    15.     if(i++ > 0) sb.Append(", ");
    16.     sb.Append($"{kv.Key.ToString()}:'{kv.Value.ToString()}'");
    17.   }
    18.   return sb.ToString();
    19. }
     
    Last edited: Feb 8, 2022
    mohsenz likes this.
  10. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    Let me redeem myself. Yes, Dictionary is the best way to do it. No Linq needed.
    Edit: In fact, no need for
    IComparable<T>
    for this one.

    Code (csharp):
    1. public static List<T> GetUniqueSortedByOccurrenceCount<T>(this IList<T> list, bool descending = true) {
    2.   var dict = new Dictionary<T, int>();
    3.   var unique = new List<T>();
    4.  
    5.   // this whole thing reeks of rabbits, obviously
    6.   // it's just a bit more compact, and with a little twist on top
    7.   for(int i = 0; i < list.Count; i++) {
    8.     var key = list[i];
    9.     if(!dict.TryGetValue(key, out var count)) {
    10.       count = 0;
    11.       unique.Add(key); // add these in one go
    12.     }
    13.     dict[key] = count + 1;
    14.   }
    15.  
    16.   // now the magic of lambda capture
    17.   if(!descending)
    18.     unique.Sort( (a,b) => dict[a].CompareTo(dict[b]) );
    19.   else
    20.     unique.Sort( (a,b) => dict[b].CompareTo(dict[a]) );
    21.  
    22.   return unique;
    23. }
    Code (csharp):
    1. var occurSort = x.GetUniqueSortedByOccurrenceCount();
    2. Debug.Log($"By occurrence: {occurSort.ToCSV()}"); // d, c, a, b
     
    Last edited: Feb 8, 2022
    mohsenz likes this.
  11. AnimalMan

    AnimalMan

    Joined:
    Apr 1, 2018
    Posts:
    1,164
    On my meth you don’t need generic or linq,

    idk if these guys will all work on platforms. Android, IPhone. mac, regard Asking of system specific. Although I did not post useable code. I assume the dude knows how to do some of the stuff.
     
  12. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    Man, I don't mind you being on meth, but you're awfully loud for someone who compares values by converting them to strings.
    You're not really helping. Generics are a compile-time thing, not something that has anything to do with phones in particular. And we use generics to give support to a wider context, instead of making this work only for this one contrived example with letters.

    What's this about? Why would you need 4 empty lists?
    He didn't ask literally for a, b, c, d, that was an example.

    Such wisdom.
     
  13. AnimalMan

    AnimalMan

    Joined:
    Apr 1, 2018
    Posts:
    1,164
    Hey if it knocks off 2 digits it knocks off 2 digits, it also got me a useable 0 to 360 in eulers with the +1. I’d use it again
    It all pass through the same voltage in the end anyways.

    Anyhow bro I’m busy making asset for the asset store

    he opens the asset

    he looks at the code


    ** Bang


    I strung every vector
     
  14. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    lol you're funny xD just don't spam too much wisdomry. the point of this forum -- the way I see it -- is to give light, let people see their own way with the help of right directions and right language, however technical. some people are just kids tho and I do get your pragmatism. but you can't be like "generic bad on phone" we ought to talk more important stuff than ignite superstitions. and we ought to check how much electricity we waste. if we care at all. voltage not same, voltage more bouncy. if voltage more bouncy, we get more entropy, more heat, chaos, confusion, waste, decay and doom.

    bad AnimalMan, baad.
     
    PraetorBlue likes this.
  15. mohsenz

    mohsenz

    Joined:
    Aug 10, 2016
    Posts:
    29
    Thank you all, appreciate it!
    the method looks so cool, As I've never tried loading class into class, I'll need a time to dig in and find out how to match this dictionary to my project, :))))

    Regarding the a,b,c,d solution, Yes, that was an example, the text content I'm trying to sort, contains thousands of words, then it sounds like a mess of creating arrays of arrays of new founded items, [if I'm right!]

    before starting the topic I had some ideas to like unfold the array into a string, write a mark into the duplicated items, or use arrays of (Indexof)s (Replace)s but it seems like crazy things,
    the dictionary seems like a good solution and probably should be ok on an Android app! I'll try!

    best