Home > Enterprise >  How to order by an array of object in C# by using another array
How to order by an array of object in C# by using another array

Time:02-02

Example I have a data

Person = [
  {
    Name: "AE1",
    Country: "PH"
  },
  {
    Name: "AE2",
    Country: "LD"
  },
  {
    Name: "AE3",
    Country: "TW"
  },
]

I want to order it by country lets say I put an const array of ["TW", "PH", "LD"].

The result would be AE3, AE1, AE2.

CodePudding user response:

You can use Array.IndexOf as sort criteria:

string[] countryOrders = {"GB", "TW", "SE"};
var personsByCountry = persons.OrderBy(p => Array.IndexOf(countryOrders, p.Country));

If a country does not exists it would come first, because -1 is returned. If you don't want that:

 var personsByCountry = persons
    .Select(p => (Person: p, Order: Array.IndexOf(countryOrders, p.Country)))
    .OrderBy(x => x.Order == -1 ? 1 : 0)
    .ThenBy(x => x.Order)
    .Select(x => x.Person);

CodePudding user response:

Using Array.IndexOf() (which has O(N) complexity) within a sort will make the sort complexity O(N^2*Log(N)) rather than O(N*Log(N))complexity.

If performance is an issue you can improve this by creating a lookup dictionary to use when sorting. This will change the complexity back to O(N*Log(N)):

public class Program
{
    public static void Main()
    {
        var persons = new Person[]
        {
            new (Name: "AE1", Country: "PH"),
            new (Name: "AE2", Country: "LD"),
            new (Name: "AE3", Country: "TW")
        };

        string[] sortOrder = { "TW", "PH", "LD" };
        var lookup = new Dictionary<string, int>();

        for (int i = 0; i < sortOrder.Length; i  )
        {
            lookup[sortOrder[i]] = i;
        }

        int indexOfCountry(Person person) => // O(1) complexity.
            lookup.TryGetValue(person.Country, out int index)
                ? index 
                : -1; // Or int.MaxValue if you want missing countries at the end.

        var result = persons.OrderBy(indexOfCountry);

        Console.WriteLine(string.Join("\n", result));
    }
}

public sealed record Person (string Name, string Country);

Of course, if performance really is an issue you should use Benchmark.Net to test whether this provides a worthwhile improvement over other, simpler approaches. For smaller lists, this approach is likely to make little difference or even make things slower. You MUST execute some benchmarks if you care about performance.

  • Related