9

I have this array:

0 => array:3 [
    "product_id" => "1138"
    "product_image" => "/resources/medias/shop/products/shop-6500720--1.png"
    "product_sku" => "6500722"
  ]
1 => array:3 [
    "product_id" => "1144"
    "product_image" => "/resources/medias/shop/products/shop-6501041--1.png"
    "product_sku" => "6501046"
  ]
2 => array:3 [
    "product_id" => "113"
    "product_image" => "/resources/medias/shop/products/shop-6294909--1.png"
    "product_sku" => "6294915"
]

What I am looking for is a way to get a multiple array with only required columns (array_column is not a option, since it's give me only 1 column).

What I have done

function colsFromArray($array, $keys)
{
    return array_map(function ($el) use ($keys) {
        return array_map(function ($c) use ($el) {
            return $el[$c];
        }, $keys);
    }, $array);
}

$array = array(
    [
        "product_id"    => "1138",
        "product_image" => "/resources/medias/shop/products/shop-6500720--1.png",
        "product_sku"   => "6500722"
    ],
    [
        "product_id"    => "1144",
        "product_image" => "/resources/medias/shop/products/shop-6501041--1.png",
        "product_sku"   => "6501046"
    ],
    [
        "product_id"    => "113",
        "product_image" => "/resources/medias/shop/products/shop-6294909--1.png",
        "product_sku"   => "6294915"
    ]
);
colsFromArray($array, array("product_id", "product_sku"));

//0 => array:3 [
//    "product_id" => "1138"
//    "product_sku" => "6500722"
//  ]
//1 => array:3 [
//    "product_id" => "1144"
//    "product_sku" => "6501046"
//  ]
//2 => array:3 [
//    "product_id" => "113"
//    "product_sku" => "6294915"
//]

The problem is that it seems too laggy, since it iterates twice over this. Is there any way to get multiple columns without this workaround?

I'm using PHP5.6

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
fiskolin
  • 1,421
  • 2
  • 17
  • 36
  • Hi @fiskolin, the question was not clear – jvk Oct 08 '18 at 16:21
  • @fiskolin can you explain where you are getting the array from? is it possible to modify the initial pull of the data? – Brandon Nelson Oct 08 '18 at 16:23
  • 1
    The bigger issue is you lose the keys, [Sandbox](http://sandbox.onlinephpfunctions.com/code/9847ab86deb6906fb09aa020867adcdf55ebd3b8) – ArtisticPhoenix Oct 08 '18 at 16:50
  • Here is the flatter version of this question: [How to filter an associative arrays using keys of an indexed array in PHP?](https://stackoverflow.com/q/4260086/2943403) Related: [Remove unwanted elements from subarrays in multidimensional array](https://stackoverflow.com/q/60886853/2943403) – mickmackusa Oct 13 '22 at 11:21
  • Related: [Get multi column from array php, Alternate of array_column](https://stackoverflow.com/q/55811947/2943403) – mickmackusa Oct 13 '22 at 11:38
  • The Laravel version of this question: [Pluck with multiple columns?](https://stackoverflow.com/q/42947515/2943403) Also topical: [Remove "columns" from the subarrays of a two dimensional array](https://stackoverflow.com/q/3702573/2943403) – mickmackusa Oct 13 '22 at 12:04

7 Answers7

15

If you need two columns from an array where one is SKU (which generally is unique) then you can use array_column with the third parameter.

$new = array_column($arr, "product_id", "product_sku");

This will return a flat array with the SKU as the key and ID as value making the array easy to work with also.

Output:

array(3) {
  [6500722]=>
  string(4) "1138"
  [6501046]=>
  string(4) "1144"
  [6294915]=>
  string(3) "113"
}

https://3v4l.org/UDGiO

Andreas
  • 23,610
  • 6
  • 30
  • 62
  • This is the correct answer to a different question. It does not provide the desired result as dictated in the question. (This is not to say that is a bad technique; it just deviates from what is required.) – mickmackusa Mar 28 '21 at 12:14
  • This technique will not be suitable if the column converted to keys has duplicated values in the original array. This doesn't look likely with the asker's sample data, but researchers should be aware of this. – mickmackusa Oct 13 '22 at 11:16
  • The advice in this answer can be found many times on earlier Stack Overflow pages, such as [here](https://stackoverflow.com/q/10328780/2943403). – mickmackusa Oct 13 '22 at 11:39
6

I think the bigger issue is you lose the keys

Original Code

array (
  0 => 
  array (
    0 => '1138',
    1 => '6500722',
  ),
  1 => 
  array (
    0 => '1144',
    1 => '6501046',
  ),
  2 => 
  array (
    0 => '113',
    1 => '6294915',
 );

You can use a simple foreach instead of the second array_map:

function colsFromArray(array $array, $keys)
{
    if (!is_array($keys)) $keys = [$keys];
    return array_map(function ($el) use ($keys) {
        $o = [];
        foreach($keys as $key){
            //  if(isset($el[$key]))$o[$key] = $el[$key]; //you can do it this way if you don't want to set a default for missing keys.
            $o[$key] = isset($el[$key])?$el[$key]:false;
        }
        return $o;
    }, $array);
}

Output

array (
  0 => 
  array (
    'product_id' => '1138',
    'product_sku' => '6500722',
  ),
  1 => 
  array (
    'product_id' => '1144',
    'product_sku' => '6501046',
  ),
  2 => 
  array (
    'product_id' => '113',
    'product_sku' => '6294915',
  ),
)

Sandbox

the problem is that it seems too laggy, since it iterates twice over this.

There is no real way to not iterate over it 2 times, but you probably don't want to throw away the keys either.

That said you can recursively unset the items you don't want.

function colsFromArray(array &$array, $keys)
{
    if (!is_array($keys)) $keys = [$keys];
    foreach ($array as $key => &$value) {
        if (is_array($value)) {
            colsFromArray($value, $keys); //recursive
        }else if(!in_array($key, $keys)){
           unset($array[$key]); 
        }
    }
}

colsFromArray($array, array("product_id", "product_sku"));
var_export($array);

Same output as before

This is easier to do by reference. Rather or not that is faster you'll have to test the 2 and see.

Sandbox

As a final note you shouldn't assume the key will exist or that keys will be an array unless you type cast it as an array.

You could also do it with array filter

function colsFromArray(array $array, $keys)
{
    if (!is_array($keys)) $keys = [$keys];
    $filter = function($k) use ($keys){
       return in_array($k,$keys);
    };
    return array_map(function ($el) use ($keys,$filter) {
        return array_filter($el, $filter, ARRAY_FILTER_USE_KEY );
    }, $array);
}

There is some small performance benefit to declaring the function for filtering outside of the loop (array_map).

Sandbox

ArtisticPhoenix
  • 21,464
  • 2
  • 24
  • 38
6

If you do not want to change your original array and want your desired output

Use array_insersect_key function to get your desired output as following

$array = array(
    [
        "product_id"    => "1138",
        "product_image" => "/resources/medias/shop/products/shop-6500720--1.png",
        "product_sku"   => "6500722"
    ],
    [
        "product_id"    => "1144",
        "product_image" => "/resources/medias/shop/products/shop-6501041--1.png",
        "product_sku"   => "6501046"
    ],
    [
        "product_id"    => "113",
        "product_image" => "/resources/medias/shop/products/shop-6294909--1.png",
        "product_sku"   => "6294915"
    ]
);

$keys = array("product_id"=>1, "product_sku"=>2);

$filteredArray = array_map(function($a) use($keys){
    return array_intersect_key($a,$keys);
}, $array);

print_r($filteredArray);
Chayan
  • 604
  • 5
  • 12
  • Really clean solution, you can also use `array_flip()` to get array values as keys to filter by too! – Othyn Dec 09 '19 at 10:36
4

I refactored the elegant approach from @Chayan into a function so it can be used like array_column(). Keys to be filtered can now be presented as a simple array.

Btw this is most likely also the fastest approach, since it uses build-in functions for most of the heavy lifting.

function array_columns(array $arr, array $keysSelect)
{    
    $keys = array_flip($keysSelect);
    return array_map(
        function($a) use($keys) {
            return array_intersect_key($a,$keys);
        },
        $arr
    );
}

$arr = [
    [
        "product_id"    => "1138",
        "product_image" => "/resources/medias/shop/products/shop-6500720--1.png",
        "product_sku"   => "6500722"
    ],
    [
        "product_id"    => "1144",
        "product_image" => "/resources/medias/shop/products/shop-6501041--1.png",
        "product_sku"   => "6501046"
    ],
    [
        "product_id"    => "113",
        "product_image" => "/resources/medias/shop/products/shop-6294909--1.png",
        "product_sku"   => "6294915"
    ]
];

$keysSelect = ["product_id" , "product_sku"];
$filteredArray = array_columns($arr, $keysSelect);

var_dump($filteredArray);
mickmackusa
  • 43,625
  • 12
  • 83
  • 136
Erik Kalkoken
  • 30,467
  • 8
  • 79
  • 114
1

If I understand your question correctly, you could try a traditional foreach - it might be a little faster.

function colsFromArray($array, $filterKeys) {
    $newArr = [];
    foreach($array as $val) {
       $element = [];
       foreach($filterKeys as $filterKey) {
          $element[$filterKey] = $val[$filterKey];
       }
       $newArr[] = $element;
    }
}

(Not tested)

The problem is that it seems too laggy, since it iterates twice over this

Your original code isn't iterating twice over the same array. You won't be able to get around iterating over the main array and then the filterKeys array if you want to have an array where each element is another array of elements with keys from the filterKeys array.

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
Nathan
  • 11,814
  • 11
  • 50
  • 93
0

This is a refactored function based on Chayan's with added renaming of selected columns:


 /** Function - array_columns  Selects columns from multidimantional array and renames columns as required
 *
 * @param  array $arr, array $selectColRenameKeys 
 *            example: (NewName1->colNameneeded1,NewName2->colNameneeded2,ect...)
 * @return array
 * @access public
 * 
 */   

 private function array_columns( $arr,$selectColRenameKeys) {    
    $keys = array_flip($selectColRenameKeys);
    $filteredArray = array_map(function($a) use($keys){
                                  $data = array_intersect_key($a,$keys);
                                  $rename_arr= array();
                                  foreach ($data as $colname => $value){
                                    $r_arr[$keys[$colname]]= $value   ;
                                  }
                                  return $r_arr;
                               }, $arr);

    return $filteredArray;
}
0

An added feature to the array_columns function that eventually traces back to Chayan's answer, this time extended from Joseph Mangion's function.

I occasionally have long lists of the selected columns where I want to preserve the keys and don't necessarily want to follow the cumbersome ['orignal_field_name'] => ['original_field_name'] format for a great number of fields.

This version preserves the original key for each field by default unless a new key is specified.

// See answer from Joseph Mangion: https://stackoverflow.com/questions/52706383/php-get-multiple-columns-from-array
/** Function - array_columns  Selects columns from multidimensional array and renames columns as required
*
* @param  array $in_array, array $select_columns_rename_keys
*   example of $select_columns_rename_keys:
*       ['new_column_name1' => 'original_column_name1', 'original_column_name2', 'original_column_name3', 'new_column_name4' => 'original_column_name4', ...]
*       This will use the original keys for columns 2 and 3 and rename columns 1 and 4
* @return array
* @access public
* 
*/   

public function array_columns($in_array, $select_columns_rename_keys) {
    foreach ($select_columns_rename_keys as $k => $v)
        if (is_int($k)) {
            $select_columns_rename_keys[$v] = $v;
            unset($select_columns_rename_keys[$k]);
        }
    $keys = array_flip($select_columns_rename_keys);
    $filtered_array =
        array_map(function($a) use($keys) {
        $data = array_intersect_key($a, $keys);
        $return_array = [];
        foreach ($data as $column_name => $value) $return_array[$keys[$column_name]] = $value;
        return $return_array;
    }, $in_array);

    return $filtered_array;
}
Loren Maxwell
  • 307
  • 3
  • 12