Sort a flat associative array by its keys according to the key order of another associative array
Asked Answered
W

5

6

I have two arrays.

One is a larger bit of data:

Array
(
    [12] => blah
    [36] => foo
    [58] => blah
    [60] => blah
    [72] => blah
    [90] => bar
)

The other is a smaller subset of different but related data in a different order, with each key corresponding to the same key in the larger array:

Array
(
    [36] => foo data
    [90] => bar data
    [12] => blah data
)

Now, my question is, how do I get the first array to be in such an order so that the keys in the first array that have corresponding keys in the second array will appear first and in the same order as the second array?

Thus, like this:

Array
(
    [36] => foo
    [90] => bar
    [12] => blah
    [58] => blah
    [60] => blah
    [72] => blah
)
Witkowski answered 27/6, 2011 at 22:10 Comment(2)
array_sort might work for youArchy
You probably have to use uksort but it is a bit difficult without using closures I think. Which PHP version are you using?Dappled
S
1
$array1 = array(12 => 1, 36 => 2, 58 => 3, 60 => 4, 72 => 5);
$array2 = array(36 => 1, 60 => 2, 12 => 1);

# obtaining keys in the order of question    
$result = array_intersect_key($array2, $array1);

# assign values from original $array1
foreach($result as $key => &$value) {
    $value = $array1[$key];
}
unset($value); # kill reference for safety

# add missing elements from $array1
$result += $array1;

var_dump($result);

Output:

array(5) {
  [36]=>
  int(2)
  [60]=>
  int(4)
  [12]=>
  int(1)
  [58]=>
  int(3)
  [72]=>
  int(5)
}

See Array Operators for the + for arrays.

Sextet answered 27/6, 2011 at 23:4 Comment(0)
P
5

Simple O(n) solution.

$arr1 = array(12 => 1, 36 => 2, 58 => 3, 60 => 4, 72 => 5);
$arr2 = array(36 => 1, 60 => 2, 12 => 1);

$result = array();

foreach($arr2 as $key => $value) {
    $result[$key] = $arr1[$key];
    unset($arr1[$key]);
}

foreach($arr1 as $key => $value) {
    $result[$key] = $arr1[$key];
}

var_dump($result);

Output:

array(5) {
  [36]=>
  int(2)
  [60]=>
  int(4)
  [12]=>
  int(1)
  [58]=>
  int(3)
  [72]=>
  int(5)
}
Potboy answered 27/6, 2011 at 22:20 Comment(2)
Was about to post approximatively the same ;)Summertree
No need to unset values and no need to copy over all duplicate items in another for-each.Sextet
L
2

use uksort

edit: fixed syntax/logic errors pointed out by malko. thank you.

$array_to_sort = array
(
    12 => "blah",
    36 => "foo",
    58 => "blah",
    60 => "blah",
    72 => "blah",
    90 => "bar"
);

$sorted_array = array(
    36 => "foo data",
    90 => "bar data",
    12 => "blah data"
);

global $sorted_array_keys;
$sorted_array_keys = array_keys($sorted_array);

function cmp($a, $b)
{
    global $sorted_array_keys;
    $a_in_array = in_array($a, $sorted_array_keys);
    $b_in_array = in_array($b, $sorted_array_keys);
    if ($a_in_array && $b_in_array) {
        return array_search($a, $sorted_array_keys) - array_search($b, $sorted_array_keys);
    } else if ( $a_in_array ) {
        return -1;
    } else {
        return 1;
    }
}

uksort ( $array_to_sort , cmp );
print_r($array_to_sort);

This started off nice and clean, but ended up pretty ugly and unclear. I now I'm leaning toward some of the other answers rather than mine.

Lansing answered 27/6, 2011 at 22:17 Comment(8)
sorry for my bad vote (removed it) yes may be a solution but you should give more detail.Summertree
@Summertree - I just posted an example that should do what was asked.Lansing
@jacobs,one of the reason this won't work, is that function cmp don't know $keys at all. See my post to see a working example with uksort (using "closure" and "use" to let the compare function know the $keys)Summertree
@Summertree Ah. I forget about php scopes. I'm usually working in as3 which has good support for closures. But, php should work with a global. See edit.Lansing
@jacobs, sorry but still not good ;) If you allow me to comment your code i'll point out what is bad.Summertree
@jacobs, correcting the syntax will result in returning: Array ( [12] => blah [90] => bar [72] => blah [58] => blah [36] => foo [60] => blah ) not what we wantSummertree
@malko. yep. i got it fixed and tested now, but now the code is so ugly. so much easier in as3...Lansing
@jacob so now it's not a -1 but a +1 ;)Summertree
S
2

Here's an example using uksort with closure, it should be more effective on big array i think, but i haven't done any benchmark so... difficult to really confirm w/o test.

$a = array(
    12 => 'blah'
    ,36 => 'foo'
    ,58 => 'blah'
    ,60 => 'blah'
    ,72 => 'blah'
    ,90 => 'bar'
);

$b = array(
    36 => 'foo data'
    ,90 => 'bar data'
    ,12 => 'blah data'
);

$keysPosition = array_flip(array_keys($b));
uksort($a,function($a,$b) use ($keysPosition){
    if(isset($keysPosition[$a],$keysPosition[$b])){
        return $keysPosition[$a]>$keysPosition[$b]?1:-1;
    }else if( isset($keysPosition[$a]) ){
        return -1;
    }else if( isset($keysPosition[$b]) ){
        return 1;
    }
    return 0;
});

print_r($a);

result:

Array
(
    [36] => foo
    [90] => bar
    [12] => blah
    [72] => blah
    [58] => blah
    [60] => blah
)

If you can't use closure (php <5.3) you can do something similar using a global but it's not clean at all.

Summertree answered 27/6, 2011 at 22:42 Comment(0)
S
1
$array1 = array(12 => 1, 36 => 2, 58 => 3, 60 => 4, 72 => 5);
$array2 = array(36 => 1, 60 => 2, 12 => 1);

# obtaining keys in the order of question    
$result = array_intersect_key($array2, $array1);

# assign values from original $array1
foreach($result as $key => &$value) {
    $value = $array1[$key];
}
unset($value); # kill reference for safety

# add missing elements from $array1
$result += $array1;

var_dump($result);

Output:

array(5) {
  [36]=>
  int(2)
  [60]=>
  int(4)
  [12]=>
  int(1)
  [58]=>
  int(3)
  [72]=>
  int(5)
}

See Array Operators for the + for arrays.

Sextet answered 27/6, 2011 at 23:4 Comment(0)
F
1

Because all keys of your $sorted_array data are found in the $array_to_sort, you don't need a sorting algorithm. Just replace the entire payload of $sorted_array with the entire payload of $array_to_sort. PHP will honor the first array's key order while replacin values, then will append the other unmentioned associative elements.

Code: (Demo)

var_export(
    array_replace($sorted_array, $array_to_sort)
);

Yes, it is just that simple.


If your sorting array may have keys that are not found in the array to be sorted, then leveraging a lookup array with uksort() is sensible. (Demo)

$priority = array_flip(array_keys($sorted_array));
uksort(
    $array_to_sort,
    fn($a, $b) => ($priority[$a] ?? PHP_INT_MAX) <=> ($priority[$b] ?? PHP_INT_MAX)
);
var_export($array_to_sort);

Neither of my above scripts guarantee that the outliers are sorted ascending. If this is needed, add a secondary sorting rule. (Demo)

$priority = array_flip(array_keys($sorted_array));
uksort(
    $array_to_sort,
    fn($a, $b) => [($priority[$a] ?? PHP_INT_MAX), $a] <=> [($priority[$b] ?? PHP_INT_MAX), $b]
);
var_export($array_to_sort);
Fountain answered 18/3 at 22:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.