Howard
Howard

Reputation: 3758

How can I combine this Laravel Collection sort?

This is what I currently have:

    use Illuminate\Support\Collection;

    $order = ['Land', 'Planeswalker', 'Creature', 'Instant', 'Sorcery', 'Enchantment', 'Artifact'];
    $landOrder = ['Basic', ''];

    $deck = $deck->sortBy(function($card) use ($landOrder) {
        foreach ($landOrder as $index => $supertypes) {
            if (str_contains($card->supertypes, $supertypes)) {
                return $index;
            }
        }
    })->values();

    $deck = $deck->sortBy(function($card) use ($order) {
        foreach ($order as $index => $type) {
            if (str_contains($card->types, $type)) {
                return $index;
            }
        }
    })->values();

Basically I'd like to sort by $order first and then tweak the order based on $landOrder.

Currently it sorts by whichever function was executed second. How can I make it so it sorts for both?

Upvotes: 1

Views: 403

Answers (1)

patricus
patricus

Reputation: 62228

I would use the sort method instead of sortBy. The sort method uses the PHP uasort function, which gives you a little more control over the sorting functionality.

$deck->sort(function($a, $b) use ($order, $landOrder) {
    // get the order indexes for the two items
    $ai = array_search($a->types, $order);
    $bi = array_search($b->types, $order);

    // if the two items have the same order, drill down to the land order comparison
    if ($ai === $bi) {
        // get the land order indexes for the two items
        $aii = array_search($a->supertypes, $landOrder);
        $bii = array_search($b->supertypes, $landOrder);

        // order based on "natural order" comparison of land order indexes
        return strnatcmp($aii, $bii);
    }

    // order based on "natural order" comparison of order indexes
    return strnatcmp($ai, $bi);
})->values();

Edit

You'll need to update the function to implement whatever logic is needed to get the correct index from the $order and $landOrder arrays. I just used array_search() as a quick and easy function, but this assumes that the value of the types and supertypes attributes would be exact matches to the entries in the arrays (i.e. 'Land' and 'Basic', not '["Land"]' and '["Basic"]').

Based on the logic from your question, and the format of the attributes from your comments, you're probably looking for something like this:

$deck->sort(function($a, $b) use ($order, $landOrder) {
    // get the order indexes for the two items
    $ai = null;
    $bi = null;
    foreach ($order as $index => $type) {
        if (str_contains($a->types, $type)) {
            $ai = $index;
        }
        if (str_contains($b->types, $type)) {
            $bi = $index;
        }
    }
    // if the type is not in the array, assign some type of value that will order like types together, but after all other proper types
    if (is_null($ai)) {
        $ai = count($order).$a->types;
    }
    if (is_null($bi)) {
        $bi = count($order).$b->types;
    }

    // if the two items have the same order, drill down to the land order comparison
    if ($ai === $bi) {
        // get the land order indexes for the two items
        $aii = null;
        $bii = null;
        foreach ($landOrder as $index => $supertype) {
            if (str_contains($a->supertypes, $supertype)) {
                $aii = $index;
            }
            if (str_contains($b->supertypes, $supertype)) {
                $bii = $index;
            }
        }
        // if the supertype is not in the array, assign some type of value that will order like supertypes together, but after all other proper supertypes
        if (is_null($aii)) {
            $aii = count($landOrder).$a->supertypes;
        }
        if (is_null($bii)) {
            $bii = count($landOrder).$b->supertypes;
        }

        // order based on "natural order" comparison of land order indexes
        return strnatcmp($aii, $bii);
    }

    // order based on "natural order" comparison of order indexes
    return strnatcmp($ai, $bi);
})->values();

Upvotes: 1

Related Questions