How to do custom Sorting in a List?

I have a ScriptableObject in unity

[CreateAssetMenu(fileName = "New Food", menuName = "Prismalia/ScriptableObject/Food")]
public class FoodScriptableObject : ScriptableObject, IComparable
{
[Header("Basic Info")]
public string itemID;
public new string name;

[Header("Main food stats")]
public string MainEffect;
public int MainEffectValue;
public bool MainValuePercent = false;
public int Amount;

[Header("Sub food stats")]
public string SubEffect;
public int SubEffectValue;
public bool SubValuePercent = false;

public string Description;

[Header("Enums")]
public Rarity Rarity;
public FoodType FoodType;
public MainFoodEffect MainFoodEffect;
public FoodEffectsTypes MainFoodEffectType;
public SubFoodEffect SubFoodEffect;
public FoodEffectsTypes SubFoodEffectType;

[Header("Visuals")]
public Sprite MainSprite;
public Sprite SubSprite;
public GameObject Prefab;

[Header("Arrays")]
public string[ ] RefinementRankDescription;

public int CompareTo(object obj)
{
var a = this;
var b = obj as FoodScriptableObject;

if (a.Amount < b.Amount)
return -1;

if (a.Amount > b.Amount)
return 1;

return 0;
}

}

I save a lot of them in a list on a other Scriptnd here I sort it based on the amount

if(bSort)
{
scripts.Sort();
}

How can I make it sort acd and dec? based on the amount and how can I make it sort from a diffren variable or an enum for intance sort by rarity?

public enum Rarity
{
Common,
Uncommon,
Rare,
Epic,
Legendary,
Mythic
}

If any one helps please try not to use lamda typing as I dont understand lamda

I tried making a new list and coping it over to it as I have multiple scriptable objects and I save all in a normal List and get the type by GetType() then use the values

Various flavors of array and list sort have overloads that can take a keying or comparison predicate.

If you post a code snippet, ALWAYS USE CODE TAGS:

How to use code tags: Using code tags properly

Managed to sort it based on rarity but idk if it is really optimised

bSort = false;
List obj = new List();
for (int i = 0;i < scripts.Count;i++)
{
obj.Add((FoodScriptableObject)scripts*);*
}
var sortedList = obj.OrderByDescending(x => (int)(x.Rarity)).ToList();
scripts.Clear();
for (int i = 0; i < sortedList.Count;i++)
{
scripts.Add(sortedList*);*
}

You can do it in just one line:

scripts = scripts.OrderByDescending(x => x.Rarity).ToList();

For performance, it should be ok, as long as you don’t have thousands of items in the list or you’re not running it 100 times per frame…

You’re still not using code tags… use code tags:

Also in your code you’re using linq to order the list and then generating a whole new list. Does it work? Yes. Is it the best way to go about it? Not particularly since you’re literally generating a whole new list.

As Nad_B pointed out it you’re not doing this often it may not matter. But if you do become concerned about it… as Kurt said list and array’s sort methods have overloads for sorting custom wise.

Here is the documentation for List.Sort (it appears ā€˜scripts’ in this case are in a List):

The documentation includes examples.

In your case something like:

lst.Sort((a, b) => a.Rarity.CompareTo(b.Rarity));

sort ascending by rarity

lst.Sort((a, b) => b.Rarity.CompareTo(a.Rarity));
//or
lst.Sort((a, b) => -a.Rarity.CompareTo(b.Rarity));

sorts descending by rarity

This will sort your list in place rather than creating a new list.

(also note how much easier the code is to read in code tags)

5 Likes

Using SortedSet is another option:

SortedSet<FoodScriptableObject> foodsByAmount = new(new SortByAmount());
SortedSet<FoodScriptableObject> foodsByRarity = new(new SortByRarity());

private sealed class SortByAmount : IComparer<FoodScriptableObject>
{
    public int Compare(FoodScriptableObject a, FoodScriptableObject b) => a.Amount.CompareTo(b.Amount);
}

private sealed class SortByRarity : IComparer<FoodScriptableObject>
{
    public int Compare(FoodScriptableObject a, FoodScriptableObject b) => a.Rarity.CompareTo(b.Rarity);
}
1 Like

IComparers could also be used with normal lists:

private readonly SortByAmount _sortByAmount = new();
private readonly SortByRarity _sortByRarity = new();

// ...
list.Sort(_sortByAmount);
list.Sort(_sortByRarity);

BTW the C# compiler creates (and caches) under the hood an IComparer (Comparison) and uses it when using the .Sort(lambda) shown by @lordofduct

2 Likes