martinszeltins's avatar

How to merge 2 collections together?

I have 2 collections that I would like to merge but the merge() method doesn't seem to do what I expect.

Collection one

$collection_one = collect([
    'user_id' => 1,
    'username' => johnive3,
    'posts' => 569,
]);

  // 0 => array:3 [
  //   "user_id" => 1
  //   "username" => "johnive3"
  //   "posts" => 569
  // ],

  // 1 => array:3 [
  //   "user_id" => 105
  //   "username" => "larry_one"
  //   "posts" => 81
  // ],

  // 2 => array:3 [
  //   "user_id" => 7
  //   "username" => "sally25"
  //   "posts" => 1
  // ]

Collection two (added comments for each user)

$collection_two = collect([
    'user_id' => 1,
    'username' => johnive3,
    'comments' => 12,
]);

// 0 => array:3 [
//   "user_id" => 1
//   "username" => "johnive3"
//   "comments" => 12
// ],

// 1 => array:3 [
//   "user_id" => 105
//   "username" => "larry_one"
//   "comments" => 84
// ],

// 2 => array:3 [
//   "user_id" => 7
//   "username" => "sally25"
//   "comments" => 984
// ]

Expected result

[
    'user_id' => 1,
    'username' => johnive3,
    'posts' => 569
    'comments' => 12,
]

// 0 => array:3 [
//   "user_id" => 1
//   "username" => "johnive3"
//   "posts" => 569
//   "comments" => 12
// ],

// 1 => array:3 [
//   "user_id" => 105
//   "username" => "larry_one"
//   "posts" => 81
//   "comments" => 84
// ],

// 2 => array:3 [
//   "user_id" => 7
//   "username" => "sally25"
//   "posts" => 8
//   "comments" => 984
// ]
0 likes
8 replies
Sinnbeck's avatar

Strange. It works for me.

$collection_one = collect([
    'user_id' => 1,
    'username' => 'johnive3',
    'posts' => 569,
]);

$collection_two = collect([
    'user_id' => 1,
    'username' => johnive3,
    'comments' => 12,
]);

$merged = $collection_one->merge($collection_two);


Result

 [
       "user_id" => 1,
       "username" => "johnive3",
       "posts" => 569,
       "comments" => 12,
     ]
Punksolid's avatar

Hi @martinzeltin is it a multidimensional array I think? And you want to merge all the properties based on the user_id right?

You could try this

$collection_with_posts;
$collection_with_comments;

$integrated = [];
foreach($collection_with_posts as $post_arr) { 
    $collection_comment_arr = $collection_with_comments->where('user_id', $post_arr['user_id']);
    $integrated[] = array_merge($post_arr, $collection_comment_arr);
}

return $integrated;

You need something like that, this code is not tested. And secondly take in mind that this could be resolved more performantly on the query itself if you have that in your database. However it should work something like that.

martinszeltins's avatar

@resin try this, it isn't working....

        $array1[] = [
            'user_id' => 1,
            'username' => 'johnive3',
            'posts' => 569,
        ];

        $array1[] = [
            'user_id' => 2,
            'username' => 'elliot',
            'posts' => 9,
        ];

        $array2[] = [
            'user_id' => 1,
            'username' => 'johnive3',
            'comments' => 1569,
        ];

        $array2[] = [
            'user_id' => 2,
            'username' => 'elliot',
            'comments' => 19,
        ];

        $collection_one = collect($array1);
        $collection_two = collect($array2);

        $merged = $collection_one->merge($collection_two);

        dump($merged->all());

result

array:4 [
  0 => array:3 [
    "user_id" => 1
    "username" => "johnive3"
    "posts" => 569
  ]
  1 => array:3 [
    "user_id" => 2
    "username" => "elliot"
    "posts" => 9
  ]
  2 => array:3 [
    "user_id" => 1
    "username" => "johnive3"
    "comments" => 1569
  ]
  3 => array:3 [
    "user_id" => 2
    "username" => "elliot"
    "comments" => 19
  ]
]

geraintp's avatar

I think @martinzeltin problem, is that he's missing a set of brackets in his example? and that each collection is an array of arrays or models..

eg

    $collection_one = collect([[
        'user_id' => 1,
        'username' => 'johnive3',
        'posts' => 569,
    ]]);

    $collection_two = collect([[
        'user_id' => 1,
        'username' => 'johnive3',
        'comments' => 12,
    ]]); 

in which case a straight merge wouldn't work. you'd have to do a map / translate and find.

    $collection_one = collect([[
        'user_id' => 1,
        'username' => 'johnive3',
        'posts' => 569,
    ]]);

    $collection_two = collect([[
        'user_id' => 1,
        'username' => 'johnive3',
        'comments' => 12,
    ]]);  

    dump($collection_one->merge($collection_two));

===

Collection {#243 
  #items: array:2 [
    0 => array:3 [
      "user_id" => 1
      "username" => "johnive3"
      "posts" => 569
    ]
    1 => array:3 [
      "user_id" => 1
      "username" => "johnive3"
      "comments" => 12
    ]
  ]
}



    
    $merged = $collection_one->map(function ($item) use ($collection_two){
        return  array_merge($item, $collection_two->firstWhere('user_id','===', $item['user_id']));
    });

    dump($merged);

===
Collection {#248 
  #items: array:1 [
    0 => array:4 [
      "user_id" => 1
      "username" => "johnive3"
      "posts" => 569
      "comments" => 12
    ]
  ]
}

    // or 

    $collection_one->transform(function ($item) use ($collection_two){
        return  array_merge($item, $collection_two->firstWhere('user_id','===', $item['user_id']));
    });

    dump($collection_one);

Collection {#232 
  #items: array:1 [
    0 => array:4 [
      "user_id" => 1
      "username" => "johnive3"
      "posts" => 569
      "comments" => 12
    ]
  ]
}

martinszeltins's avatar

@geraintp yes, it looks like this is what I was looking for (merging on user_id).

$merged = $col_one->map(function ($item) use ($col_two) {
      return  array_merge($item, $col_two->firstWhere('user_id', $item['user_id']));
 });

I guess it would be nice if there was a collection method that allowed to merge on key like this...

   $merged = $collection_one->merge($collection_two)->on('user_id');

isn't that elegant?

danyelkeddah's avatar
Level 13

@martinzeltin try this one, it will be grouped by username

<?php

$all = $collection_one->merge($collection_two)->groupBy('username', false)->map(function ($value, $key) {
        return $all[$key] = array_merge(...$value);
    });

result

Collection {#231 ▼
  #items: array:3 [▼
    "johnive3" => array:4 [▼
      "user_id" => 1
      "username" => "johnive3"
      "posts" => 569
      "comments" => 12
    ]
    "larry_one" => array:4 [▼
      "user_id" => 105
      "username" => "larry_one"
      "posts" => 81
      "comments" => 84
    ]
    "sally25" => array:4 [▼
      "user_id" => 7
      "username" => "sally25"
      "posts" => 1
      "comments" => 984
    ]
  ]
}
1 like

Please or to participate in this conversation.