CXJ
CXJ

Reputation: 4469

PHP sorting array_intersect_key() results by second array

I have a method in a class that looks like this;

class SomeClass {
    private $hidden = array(....);

    /**
     * @return array - numeric indexed array in order of $this->hidden.
     * Suitable for use by list(var1, var2, ...)
     */
    public function getAsList($list = array())
    {
       return array_values(array_intersect_key($this->hidden, array_flip($list) );
    }

But this is not useful, since the caller of the method does not know the order of the key/element pairs in the associative array in instance variable $hidden. Ideally, the returned array would be in the exact same order as the keys specified in $list. For example:

$foo = new SomeClass();
list($foo, $bar, $baz) = $foo->getAsList(array('foo', 'bar', 'baz');

I can easily write some explicit, verbose PHP code in a loop to do this, but is there some clever way to use the various array functions, e.g. array_multisort() to spit this out in minimal lines of code (and hopefully, at compiled code speed -- I'll test it, if it matters).

In a sense, this is a brain teaser to which I don't yet know the answer. It's not critical I do it without an explicit loop, but I'm curious as to if it can be done. I spent 30 or so minutes on it, and haven't found a solution yet.

Upvotes: 14

Views: 2623

Answers (2)

mickmackusa
mickmackusa

Reputation: 47991

This is one of those cases when functional programming pales in comparison to a language construct in terms of readability, maintanability, and efficiency.

I have a bias toward functional programming, but in this case it just doesn't pay.

Code: (Demo)

$hidden = [
  'apples' => 19,
  'eggs' => 7,
  'grapes' => 144,
  'mushrooms' => 3,
  'oranges' => 16
];

$list = ['grapes', 'apples', 'eggs', 'oranges'];
foreach ($list as $item) {
    if (isset($hidden[$item])) {
        $result[] = $hidden[$item];
    }
}

var_export($result);

Output:

array (
  0 => 144,
  1 => 19,
  2 => 7,
  3 => 16,
)

If you insist on using functional programming, then it would be most efficient to perform the required operations in this order:

  • filter the array
  • order the keys of the filtered array
  • reindex the ordered, filtered array

Here is how:

Code: (Demo)

$flippedList = array_flip($list);
var_export(array_values(array_replace($flippedList, array_intersect_key($hidden, $flippedList))));

(same output as previous snippet)

Logically, it doesn't make sense to order an array that has excess elements in it. Lighten the load, first.

Upvotes: 2

Paul
Paul

Reputation: 141877

Perhaps array_replace is the missing piece to your puzzle:

public function getAsList($list = array())
{
  $klist = array_flip($list);
  return array_values(array_intersect_key(array_replace($klist, $this->hidden), $klist));
}

Example (Demo):

$hidden = [
  'apples' => 19,
  'eggs' => 7,
  'grapes' => 144,
  'mushrooms' => 3,
  'oranges' => 16
];

$list = ['grapes', 'apples', 'eggs', 'oranges'];

$klist = array_flip($list);
print_r(array_values(array_intersect_key(array_replace($klist, $hidden), $klist)));

/*
Array
(
    [0] => 144
    [1] => 19
    [2] => 7
    [3] => 16
)
*/

Upvotes: 18

Related Questions