powershellsortingenumerable

Resort one array based on another array


I am looking to resort one array based on another array. In a previous project I needed to just get a list of the disordered items, and applying that code to my current scenario I get this

$definedSet = @('C', 'B', 'D', 'A')
$history = @('A', 'B', 'C', 'D')

$disordered = $history.Where({-not [Linq.Enumerable]::SequenceEqual([string[]]$history, [string[]]$definedSet)})
$disordered

Which does indeed give me a list of all four items, because they are all out of order. However, in this new scenario I need to resort $history based on $definedSet. The key being that there could be items in one that aren't in the other. But I am starting with a simpler problem, and that has me stumped. I feel certain [Linq.Enumerable] is the key, obviously, but my Google-Fu is not pointing me towards a solution. I have tried the Microsoft Docs article on the Enumerable class, and my brain... melted.


Solution

  • In this case you can sort by index, using Array.IndexOf:

    OverloadDefinitions
    -------------------
    int IList.IndexOf(System.Object value)
    

    However it's worth noting this method is case-sensitive. If you wish to find indexes with a case-insensitive method you can use Array.FindIndex:

    OverloadDefinitions
    -------------------
    static int FindIndex[T](T[] array, System.Predicate[T] match)
    static int FindIndex[T](T[] array, int startIndex, System.Predicate[T] match)
    static int FindIndex[T](T[] array, int startIndex, int count, System.Predicate[T] match)
    

    Or you can initialize the set as a List<T> and use it's FindIndex(Predicate<T>) method.

    Both options should use a case-insensitive equality comparer (-eq / -ne) in their Predicate<T>.

    Sort-Object allows you to sort by multiple expressions, in the example below it will sort first by the found index in the set and then alphabetically:

    $definedSet = 'powershell', 'is', 'awesome'
    $history = 'Awesome', 'PowerShell', 'set', 'not in', 'is'
    $predicate = [Predicate[string]] { $args[0] -eq $_ }
    $history | Sort-Object { [array]::FindIndex($definedSet, $predicate) }, { $_ }