2

I have a array with repeated values like this:

[
    [
        'id' => 112,
        'name' => 'John',
        'country' => 'Spain',
        'age' => 24,
        'company' => 'One',
        'price' => 10
    ],
    [
        'id' => 112,
        'name' => 'John',
        'country' => 'Spain',
        'age' => 24,
        'company' => 'Two',
        'price' => 15
    ],
    [
        'id' => 112,
        'name' => 'John',
        'country' => 'Spain',
        'age' => 24,
        'company' => 'Three',
        'price' => 20
    ],
    [
        'id' => 224,
        'name' => 'Paul',
        'country' => 'France',
        'age' => 25,
        'company' => 'One',
        'price' => 25
    ],
    [
        'id' => 224,
        'name' => 'Paul',
        'country' => 'France',
        'age' => 25,
        'company' => 'Two',
        'price' => 40
    ]
]

I need to group same id with name, country and age, and make a "sub array" companies with company and price

array (
  112 => 
  array (
    'id' => 112,
    'name' => 'John',
    'country' => 'Spain',
    'age' => 24,
    'companies' => 
    array (
      0 => 
      array (
        'company' => 'One',
        'price' => 10,
      ),
      1 => 
      array (
        'company' => 'Two',
        'price' => 15,
      ),
      2 => 
      array (
        'company' => 'Three',
        'price' => 20,
      ),
    ),
  ),
  224 => 
  array (
    'id' => 224,
    'name' => 'Paul',
    'country' => 'France',
    'age' => 25,
    'companies' => 
    array (
      0 => 
      array (
        'company' => 'One',
        'price' => 25,
      ),
      1 => 
      array (
        'company' => 'Two',
        'price' => 40,
      ),
    ),
  ),
)

I have tried this code without good results:

$new_array = array();
foreach ($my_array as $item) {      
    $new_array[$item['id']][] = $item;
    $new_array[$item['id']]['companies'][] = $item;
    
}

Then I tried this:

$new_array = array();
foreach ($my_array as $item) {      
    $new_array[$item['id']]['temp'] = $item;
    $new_array[$item['id']]['companies'][] = $item;         
}

Works fine, but I get temp key that I don't want it. For example, with this code I need to access to id items with $new_array [$key]['temp']['id']

I can remove the temp key with another loop:

$final_array = array();
foreach ($new_array $key=>$item) {      
    $final_array [$key] = $item['temp'];
    $final_array [$key]['temp'] = $item['companies'];
}

With this code I can access correctly to id with: $final_array[$key]['id']

Another option is this code:

foreach($array as $v) {
    $result[$v['id']]['id']          = $v['id'];
    $result[$v['id']]['name']        = $v['name'];
    $result[$v['id']]['country']     = $v['country'];
    $result[$v['id']]['age']         = $v['age'];
    $result[$v['id']]['companies'][] = array('company' => $v['company'],
                                             'price'   => $v['price']);
}

But it is not very elegant if we had more keys (phone, email...)

Any ideas?

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
kurtko
  • 1,978
  • 4
  • 30
  • 47

4 Answers4

2

Nothing wrong with the code you've provided at the end, but here's some ideas for how to make it more tolerant to more array values (as you mentioned, phone, email, etc).

This uses the handy PHP array_diff_key function to remove the array elements you don't want from the "core" records. Then, applying array_diff_key to get those same array elements INTO the "company" records.

// set up the array of keys you don't want in the "original" record
$exclude = ['company' => FALSE, 'price' => FALSE];
// Your original loop
foreach($array as $v) {
    // not strictly necessary, but helps readability
    $id = $v['id'];
    // Assign the "core" record all the values you want, excluding those defined above
    // in this case, will remove "company" and "price"
    $record = array_diff_key( $v, $exclude );

    // only set this if not already set, otherwise wipes out previous companies
    if ( ! isset($result[$id] ) ) {
        $result[$id] = $record;
        $result[$id]['companies'] = [];
    }

    // strip off the OTHER values from the array you don't want stored with the company
    // this will automatically pick up the field NOT excluded above
    // in this case, will remove all BUT "company" and "price"
    $company = array_diff_key( $v, $record );
    $result[$id]['companies'][] = $company;
}
random_user_name
  • 25,694
  • 7
  • 76
  • 115
0

One way to rome...

$collector=array();
foreach($array as $set){
 //get a copy
 $ref = $set;
 //unset company data
 unset($ref['company'],$ref['price']);
 //make a refer
 $r=$ref['id'];
 //setup main data if not set before
 if(!isset($collector[$r])){ 
    $collector[$r]=$ref;
 } 
 //collect company data
 $collector[$r]['companies'][] = array('company'=>$set['company'],'price'=>$set['price']);
}
print_r($collector);
JustOnUnderMillions
  • 3,741
  • 9
  • 12
0

Similar to other answers:

$info = array (
    array('id' => 112, 'name' => 'John', 'country' => 'Spain', 'age' => 24, 'company' => 'One', 'price' => 10),
    array('id' => 112, 'name' => 'John', 'country' => 'Spain', 'age' => 24, 'company' => 'Two', 'price' => 15),
    array('id' => 112, 'name' => 'John', 'country' => 'Spain', 'age' => 24, 'company' => 'Three', 'price' => 20),
    array('id' => 224, 'name' => 'Paul', 'country' => 'France', 'age' => 25, 'company' => 'One', 'price' => 25),
    array('id' => 224, 'name' => 'Paul', 'country' => 'France', 'age' => 25, 'company' => 'Two', 'price' => 40)
);

$grouped = array();
foreach ($info as $item) {
    if (!isset($grouped[$item['id']])) {
        $grouped[$item['id']] = $item;
    }
    $grouped[$item['id']]['companies'][] = array($item['company'],$item['price']);
    unset($grouped[$item['id']]['company']);
    unset($grouped[$item['id']]['price']);
}

print_r($grouped);
Guillermo Phillips
  • 2,176
  • 1
  • 23
  • 40
  • The thing is that this doesn't solve the problem of "being tolerant of new fields". That's the entire purpose of the question. – random_user_name Feb 01 '17 at 17:27
  • It looks like this is probably coming from a database, in which case the id field is uniquely identifying the record, irrespective of the number of fields. My code is tolerant to that. If any new fields need to be grouped under 'companies', the code would have to be changed anyway. It's simple enough to create another array to hold the fields that need to be grouped, as I see you have done. Saying that, my code is not 'complex' to read or maintain. – Guillermo Phillips Feb 01 '17 at 17:37
0

The sample data does not seem to require grouping by more than the id column.

The snippet below will extract/consume the last two elements in each row before pushing the first encountered row with a unique id, then it unconditionally pushes those two extracted rows into the group's companies subset.

Code: (Demo)

$result = [];
foreach ($array as $row) {
    $companies = array_splice($row, -2);
    $result[$row['id']] ??= $row;
    $result[$row['id']]['companies'][] = $companies;
}
var_export($result);
mickmackusa
  • 43,625
  • 12
  • 83
  • 136