Home > Enterprise >  How do I sort a Laravel Collection by an array?
How do I sort a Laravel Collection by an array?

Time:12-09

I know this has been asked several times, but none of the answers work for me.

I have a collection of products; each has an eloquent relationship containing multiple items.

Here's how I'm retrieving the collection

$collections = Collection::with('items')->get();

And this is the result of return $collections from the controller.

[
  {
    "id": 1,
    "name": "Product One",
    "items": [
      {
        "id": 1,
        "product_id": 1,
        "name": "Item One",
      },
      {
        "id": 2,
        "product_id": 1,
        "name": "Item Two",
      },
      {
          "id": 3,
          "product_id": 1,
          "name": "Item Three",
      },
  },
  {
      "id": 2,
      "name": "Product Two",
      "items": [
        {
          "id": 1,
          "product_id": 2,
          "name": "Item One",
        },
        {
          "id": 2,
          "product_id": 2,
          "name": "Item Two",
        },
        {
          "id": 3,
          "product_id": 2,
          "name": "Item Three",
        },
    }
]

I'd like to sort each product's items in a different order.

I'd like to sort Product One's items as 3, 1, 2 and Product Two's items as 2, 3, 1.

So I created an array of the new sort order, and then added a callback

$newSortOrder = [
  1 => [3, 1, 2],
  2 => [2, 3, 1]
];

$collections = Collection::with('items')->get();

foreach ($collections as $collection) {
    $collection->items->sortBy(function ($model) use ($newSortOrder) {
        return array_search($model->id, $newSortOrder[$model->id]);
    });
}

dd($collections); <-- this is still in the default sort order from the db

However, when I return $collections, the items are still in the default order. What am I doing wrong?

Edit: tried this as well but with same results; $collection items are returned in the default order.

foreach ($collections as $collection) {
    $collection->items->sortBy(function ($model) use ($order) {
        return array_search($model->getkey(), $order);
    });
}

CodePudding user response:

$sortOrder = [
  1 => [3, 1, 2],
  2 => [2, 3, 1]
];

$sorted = $products->map(function($product) use ($sortOrder) {
    $order = $sortOrder[$product->id];
    return [ 
        ...$product,
        'items' => $product->items->mapWithKeys(
            fn($item) => [array_search($item['id'], $order) => $item]
        )->sortKeys()
    ];
});

This Outputs

Illuminate\Support\Collection {#4720
  #items: array:2 [
    0 => array:3 [
      "id" => 1
      "name" => "product one"
      "items" => Illuminate\Support\Collection {#4722
        #items: array:3 [
          0 => array:2 [
            "id" => 3
            "name" => "three"
          ]
          1 => array:2 [
            "id" => 1
            "name" => "one"
          ]
          2 => array:2 [
            "id" => 2
            "name" => "two"
          ]
        ]
        #escapeWhenCastingToString: false
      }
    ]
    1 => array:3 [
      "id" => 2
      "name" => "product two"
      "items" => Illuminate\Support\Collection {#4721
        #items: array:3 [
          0 => array:2 [
            "id" => 2
            "name" => "two"
          ]
          1 => array:2 [
            "id" => 3
            "name" => "three"
          ]
          2 => array:2 [
            "id" => 1
            "name" => "one"
          ]
        ]
        #escapeWhenCastingToString: false
      }
    ]
  ]
  #escapeWhenCastingToString: false
}

CodePudding user response:

you can recreate items using map().

          // not recommend because key does not support for correct item's id
        //   $newSortOrder = [
        //     1 => [3, 1, 2],
        //     2 => [2, 3, 1]
        //   ];

          $newSortOrder = [
            [
                "id" => 1,
                "sortOrder" => [3, 1, 2]
            ],
            [
                "id" => 2,
                "sortOrder" => [2, 3, 1]
            ],
          ];

          $collections = Collection::with('items')->get();

          $sortedCollection = collect($collections)->map(function($item) use($newSortOrder) {
            $id = data_get($item,'id');
            $name = data_get($item,'name');

            $originItems = data_get($item,'items');
            $sortOrderList = data_get(collect($newSortOrder)->firstWhere('id',$id),'sortOrder');

            $items = collect($sortOrderList)->map(function($sortNumber) use($originItems) {
                return collect($originItems)->firstWhere('id',$sortNumber);
            });
            return compact('id','name','items');
          });

          dd($sortedCollection); // result
  • Related