Skilldrick
Skilldrick

Reputation: 70819

Is there a function to extract a 'column' from an array in PHP?

I have an array of arrays, with the following structure :

array(array('page' => 'page1', 'name' => 'pagename1')
      array('page' => 'page2', 'name' => 'pagename2')
      array('page' => 'page3', 'name' => 'pagename3'))

Is there a built-in function that will return a new array with just the values of the 'name' keys? so I'd get:

array('pagename1', 'pagename2', 'pagename3')

Upvotes: 28

Views: 40830

Answers (14)

mickmackusa
mickmackusa

Reputation: 47874

Consider this sample input array to expose nuanced differences in script behaviors:

$array = [
    'a' => ['page' => 'page1', 'name' => 'pagename1'],
    'b' => ['page' => 'page2', 'name' => 'pagename2'],
    'c' => ['page' => 'page3', 'name' => 'pagename3'],
    'd' => ['page' => 'page4',],
];
  • Populate a new indexed array from column values with array_column(): (Demo)

    var_export(array_column($array, 'name'));
    # array (
    #   0 => 'pagename1',
    #   1 => 'pagename2',
    #   2 => 'pagename3',
    # )
    

    Missing column values are no problem.

  • Populate a new array preserving first level keys with array_map(): (Demo)

    var_export(array_map(fn($row) => $row['name'] ?? 'missing', $array));
    # array (
    #   'a' => 'pagename1',
    #   'b' => 'pagename2',
    #   'c' => 'pagename3',
    #   'd' => 'missing',
    # )
    

    Missing column values must be explicitly handled to avoid an error.

  • Populate a new indexed array from column values with array_reduce(): (Demo)

    var_export(array_reduce($array, fn($result, $row) => array_merge($result, [$row['name'] ?? 'missing']), []));
    # array (
    #   0 => 'pagename1',
    #   1 => 'pagename2',
    #   2 => 'pagename3',
    #   3 => 'missing',
    # )
    

    Missing column values must be explicitly handled to avoid an error.

  • Overwrite the rows of the original array with the column values with array_walk(): (Demo)

    array_walk($array, fn(&$row) => $row = $row['name'] ?? 'missing');
    var_export($array);
    # array (
    #   'a' => 'pagename1',
    #   'b' => 'pagename2',
    #   'c' => 'pagename3',
    #   'd' => 'missing',
    # )
    

    Missing column values must be explicitly handled to avoid an error.

  • If all rows contain the sought column value, then a body-less loop can be used with array destructuring to avoid declaring a temporary variable: (Demo)

    $result = [];
    foreach ($array as ['name' => $result[]]);
    var_export($result);
    

    Note that you cannot declare the first level key as a variable and use it as the key in the result array with this approach because values are assigned BEFORE keys are assigned.

    If you need to handle potentially missing columns, then a classic loop with a null coalesced default value is suitable. (Demo)

    $result = [];
    foreach ($array as $row) {
        $result[] = $row['name'] ?? 'missing';
    }
    var_export($result);
    

    If you want to merely mutate the original input array instead of generating a new array, then use a foreach loop and modify the rows by reference. (Demo)

    foreach ($array as &$row) {
        $row = $row['name'] ?? 'missing';
    }
    var_export($array);
    
  • Certainly recursion is overkill for this simple task, but for completeness, array_walk_recursive() can be used to access "leaf nodes" in the structure and collect qualifying values in the $result array by reference. (Demo)

    $colName = 'name';
    
    $result = [];
    array_walk_recursive(
        $array,
        function ($v, $k) use ($colName, &$result) {
            if ($k === $colName) {
                $result[] = $v;
            }
        }
    );
    var_export($result);
    

Upvotes: 0

PHP Hupp Technologies
PHP Hupp Technologies

Reputation: 1952

You can get column, bind key value as well:

$a = array(
          array(
            'id' => 5698,
            'first_name' => 'Peter',
            'last_name' => 'Griffin',
          ),
          array(
            'id' => 4767,
            'first_name' => 'Ben',
            'last_name' => 'Smith',
          ),
          array(
            'id' => 3809,
            'first_name' => 'Joe',
            'last_name' => 'Doe',
          )
        );

if you want only column then use:

$last_names = array_column($a, 'last_name');
print_r($last_names);
        

if you want to bind key and values then use:

$last_names = array_column($a, 'last_name', 'id');
print_r($last_names);
    

Upvotes: 0

John Conde
John Conde

Reputation: 219804

As of PHP 5.5 you can use array_column():

<?php
$samples=array(
            array('page' => 'page1', 'name' => 'pagename1'),
            array('page' => 'page2', 'name' => 'pagename2'),
            array('page' => 'page3', 'name' => 'pagename3')
            );
$names = array_column($samples, 'name');
print_r($names);

See it in action

Upvotes: 83

Anti Veeranna
Anti Veeranna

Reputation: 11583

Why does it have to be a built in function? No, there is none, write your own.

Here is a nice and easy one, as opposed to others in this thread.

$namearray = array();

foreach ($array as $item) {
    $namearray[] = $item['name'];
}

In some cases where the keys aren't named you could instead do something like this

$namearray = array();

foreach ($array as $key => $value) {
    $namearray [] = $value;
}

Upvotes: 19

Tony Vance
Tony Vance

Reputation: 134

With array_reduce:

$names = array_reduce($array, function ($carry, $item) {
    return array_merge($carry, [$item['name']]);
}, []);

Upvotes: 0

Progrock
Progrock

Reputation: 7485

Not a 'built-in', but short arrow functions make for abrreviated explicit coding (introduced in Php v7.4.) and can be used with array_map for array transformations.

Here applying a callback to each member of the array that returns the desired attribute from each subarray:

<?php
$data =
[
    ['page' => 'page1', 'name' => 'pagename1'],
    ['page' => 'page2', 'name' => 'pagename2'],
    ['page' => 'page3', 'name' => 'pagename3']
];

$names = array_map(fn($v) => $v['name'], $data);
var_export($names);

Output:

array (
    0 => 'pagename1',
    1 => 'pagename2',
    2 => 'pagename3',
  )

The OP posted this question before array_column exisited (from Php 5.5.0). This answers the original question with a short solution:

$names = array_column($data, 'name');

But a simple loop is also trite:

foreach($data as $item) $names[] = $item['name'];

Upvotes: 0

crazeyez
crazeyez

Reputation: 499

Just to extend on some of the answers here, as of PHP 5.5, array_column is what you want.

It actually has a few possible uses.

Using the sample array below, here are the different ways to use array_column.

$a = array(
    array('id' => '1', 'name' => 'Joe'),
    array('id' => '2', 'name' => 'Jane')
);

Retrieving a single column as the array

$b = array_column($a, 'name');

Would give you. Notice the auto keys starting from 0, as per a normal array.

$b[0] = 'Joe';
$b[1] = 'Jane';

Retrieving the full array with a column as the index.

$c = array_column($a, NULL, 'id');

Would result in the following.

$c[1] = array('id' => '1', 'name' => 'Joe');
$c[2] = array('id' => '2', 'name' => 'Jane');

Notice how the column I selected as the third parameter becomes the key for each item and I get the full array by setting the second parameter to null.

Of course, the final usage is to set both the 2nd and 3rd params.

$d = array_column($a, 'name', 'id');

Would give you the following.

$d[1] = 'Joe';
$d[2] = 'Jane';

I personally use the full 3 params for creating select option lists. If I have a table with my options, I query the table and get the result and pass it into this to get a list with the key as the value and the label. This is a brilliant way for building info sets that need to intersect by the index as well.

Upvotes: -1

Dinesh
Dinesh

Reputation: 75

Yes, there is a php built-in function called array_column which does what you are looking for.

You would call it something like $name_keys = array_column($array, 'name'); to get the result that you are looking for.

Please refer to the following entry in the PHP manual for more details:

http://php.net/manual/en/function.array-column.php

Upvotes: 1

papas-source
papas-source

Reputation: 1281

Well there is. At least for PHP > 5.5.0 and it is called array_column

The PHP function takes an optional $index_keyparameter that - as per the PHP website - states:

$index_key

The column to use as the index/keys for the returned array. This value may be the integer key of the column, or it may be the string key name

In the answers here, i see a stripped version without the optional parameter. I needed it, so, here is the complete function:

if (!function_exists('array_column')) {
    function array_column($array, $column, $index_key = null) {
        $toret = array();
        foreach ($array as $key => $value) {
            if ($index_key === null){
                $toret[] = $value[$column];
            }else{
                $toret[$value[$index_key]] = $value[$column];
            }
        }
        return $toret;
    }
}

Upvotes: 6

Srok
Srok

Reputation: 37

if (!function_exists('array_column')) {
    function array_column($array,$column) {
    $col = array();
    foreach ($array as $k => $v) {
        $col[]=$v[$column];
    }
    return $col;
    }
}

This should work for php versions < 5.5 and degrade in case the function exist

Upvotes: 1

Imrul
Imrul

Reputation: 3526

I don't think there is any need to have a built in function for this. There may be an array in your those array.

$samples=array(
            array('page' => 'page1', 'name' => 'pagename1'),
            array('page' => 'page2', 'name' => 'pagename2'),
            array('page' => 'page3', 'name' => 'pagename3')
            );

$output1=array();
$output2=array();
foreach($samples as $sample){
    array_push($output1,$sample['name']);
    $output2[]=array_splice($sample,1);

}

print_r($output1);
print_r($output2);

in $output1 is the output what you want if you want only to remove the 'page' indexing' part then $output2.

if you need all the values from the that array and indexes numerically the array then you can use

$array_1=array_values($samples); 

but what i understand, you didn't want this.

Upvotes: 0

VolkerK
VolkerK

Reputation: 96159

You can extend the ArrayIterator class and override the method mixed current(void).

class Foo extends ArrayIterator {
  protected $index;
  public function __construct($array, $index) {
    parent::__construct($array);
    $this->index = $index;
  }

  public function current() {
    $c = parent::current();
    return isset($c[$this->index]) ? $c[$this->index] : null;
  }
}

$a = array(
  array('page' => 'page1', 'name' => 'pagename1'),
  array('name' => '---'),
  array('page' => 'page2', 'name' => 'pagename2'),
  array('page' => 'page3', 'name' => 'pagename3')
);

$f = new Foo($a, 'page');
foreach($f as $e) {
  echo $e, "\n";
}

prints

page1

page2
page3

Upvotes: 0

Henrik Opel
Henrik Opel

Reputation: 19441

Similar to fuentesjrs solution, but a bit more generic using array_walk() with a custom callback:

// Define the callback
function extract_named_sub_elements(&$item, $key, $name) {
  $item = $item[$name];
}

// Test data
$original = array(
  array('page' => 'page1', 'name' => 'pagename1'),
  array('page' => 'page2', 'name' => 'pagename2'),
  array('page' => 'page3', 'name' => 'pagename3'),
);

// Use a copy, as array_walk() operates directly on the passed in array
$copy = $original;

// Substitute 'name' with whatever element you want to extract, e.g. 'page'
array_walk($copy, 'extract_named_sub_elements', 'name');

print_r($copy);

Upvotes: 1

fuentesjr
fuentesjr

Reputation: 52318

Here's a functional way of doing it:

$data = array(
            array('page' => 'page1', 'name' => 'pagename1'),
            array('page' => 'page2', 'name' => 'pagename2'),
            array('page' => 'page3', 'name' => 'pagename3'));

$result = array_map(create_function('$arr', 'return $arr["name"];'), $data);
print_r($result);

Upvotes: 14

Related Questions