gmask gmask - 5 days ago 7
PHP Question

Push a set of values to an array based on another unique value in the same array

Question background



Hello, I have the following array of movie crew members:

array:7 [▼
0 => array:6 [▼
"credit_id" => "52fe49dd9251416c750d5e9d"
"department" => "Directing"
"id" => 139098
"job" => "Director"
"name" => "Derek Cianfrance"
"profile_path" => "/zGhozVaRDCU5Tpu026X0al2lQN3.jpg"
]
1 => array:6 [▼
"credit_id" => "52fe49dd9251416c750d5ed7"
"department" => "Writing"
"id" => 139098
"job" => "Story"
"name" => "Derek Cianfrance"
"profile_path" => "/zGhozVaRDCU5Tpu026X0al2lQN3.jpg"
]
2 => array:6 [▼
"credit_id" => "52fe49dd9251416c750d5edd"
"department" => "Writing"
"id" => 132973
"job" => "Story"
"name" => "Ben Coccio"
"profile_path" => null
]
3 => array:6 [▼
"credit_id" => "52fe49dd9251416c750d5ee3"
"department" => "Writing"
"id" => 139098
"job" => "Screenplay"
"name" => "Derek Cianfrance"
"profile_path" => "/zGhozVaRDCU5Tpu026X0al2lQN3.jpg"
]
4 => array:6 [▼
"credit_id" => "52fe49dd9251416c750d5ee9"
"department" => "Writing"
"id" => 132973
"job" => "Screenplay"
"name" => "Ben Coccio"
"profile_path" => null
]
5 => array:6 [▼
"credit_id" => "52fe49dd9251416c750d5eef"
"department" => "Writing"
"id" => 1076793
"job" => "Screenplay"
"name" => "Darius Marder"
"profile_path" => null
]
11 => array:6 [▼
"credit_id" => "52fe49de9251416c750d5f13"
"department" => "Camera"
"id" => 54926
"job" => "Director of Photography"
"name" => "Sean Bobbitt"
"profile_path" => null
]
]


As you can see this is a list of credits I'm getting via the TMDb API. The first step of building the above array was to filter out all jobs that I don't want to display, here's how I did that:

$jobs = [ 'Director', 'Director of Photography', 'Cinematography', 'Cinematographer', 'Story', 'Short Story', 'Screenplay', 'Writer' ];

$crew = array_filter($tmdbApi, function ($crew) use ($jobs) {
return array_intersect($jobs, $crew);
});


My question



I'd like to figure out how to take the above result one step further and combine
jobs
where the
id
is the same, so as to end up with something like this, for example:

array:7 [▼
0 => array:6 [▼
"credit_id" => "52fe49dd9251416c750d5e9d"
"department" => "Directing"
"id" => 139098
"job" => "Director, Story, Screenplay"
"name" => "Derek Cianfrance"
"profile_path" => "/zGhozVaRDCU5Tpu026X0al2lQN3.jpg"
]


I have also considered ditching doing this in my logic and instead doing it in my blade template, but I'm not sure how to achieve that.

How would you accomplish this?

Answer

You could nicely use Laravel's Collection in such a situation, which has a great number of methods which will help you in this case.

First, turn this array (the one you already filtered on jobs) to a Collection:

$collection = collect($crew);

Second, group this Collection by it's ids:

$collectionById = $collection->groupBy('id');

Now, the results are grouped by the id and transformed to a Collection in which the keys correspond to the id, and the value an array of 'matching' results. More info about it here.

Finally, just a easy script that iterates through all the results for each id and combines the job field:

$combinedJobCollection = $collectionById->map(function($item) {
    // get the default object, in which all fields match
    // all the other fields with same ID, except for 'job'
    $transformedItem = $item->first();

    // set the 'job' field according all the (unique) job
    // values of this item, and implode with ', '
    $transformedItem['job'] = $item->unique('job')->implode('job', ', ');

    /* or, keep the jobs as an array, so blade can figure out how to output these
    $transformedItem['job'] = $item->unique('job')->pluck('job');
    */

    return $transformedItem;
})->values();
// values() makes sure keys are reordered (as groupBy sets the id
// as the key)

At this point, this Collection is returned:

Collection {#151 ▼
  #items: array:4 [▼
    0 => array:6 [▼
      "credit_id" => "52fe49dd9251416c750d5e9d"
      "department" => "Directing"
      "id" => 139098
      "job" => "Director, Story, Screenplay"
      "name" => "Derek Cianfrance"
      "profile_path" => "/zGhozVaRDCU5Tpu026X0al2lQN3.jpg"
    ]
    1 => array:6 [▼
      "credit_id" => "52fe49dd9251416c750d5edd"
      "department" => "Writing"
      "id" => 132973
      "job" => "Story, Screenplay"
      "name" => "Ben Coccio"
      "profile_path" => null
    ]
    2 => array:6 [▼
      "credit_id" => "52fe49dd9251416c750d5eef"
      "department" => "Writing"
      "id" => 1076793
      "job" => "Screenplay"
      "name" => "Darius Marder"
      "profile_path" => null
    ]
    3 => array:6 [▼
      "credit_id" => "52fe49de9251416c750d5f13"
      "department" => "Camera"
      "id" => 54926
      "job" => "Director of Photography"
      "name" => "Sean Bobbitt"
      "profile_path" => null
    ]
  ]
}

Note: to use this Collection as an array, use:

$crew = $combinedJobCollection->toArray();

There are multiple ways to achieve this, for example: search the array for overlapping id's, but I think this is the easiest way to achieve this.

Goodluck!

Comments