根据另一个数组对扁平关联数据进行自定义键排序

187

在PHP中是否可以做到像这样的操作?你会如何编写一个函数?这里有一个例子,顺序是最重要的。

$customer['address'] = '123 fake st';
$customer['name'] = 'Tim';
$customer['dob'] = '12/08/1986';
$customer['dontSortMe'] = 'this value doesnt need to be sorted';

我希望您能做一些类似的事情

$properOrderedArray = sortArrayByArray($customer, array('name', 'dob', 'address'));

因为最后我使用了foreach(),它们的顺序不正确(因为我将值附加到一个字符串中,该字符串需要按正确顺序排列,而我事先不知道所有的数组键/值)。

我已经查看了PHP的内部数组函数,但似乎只能按字母或数字进行排序。


16个回答

423

可以使用array_merge或者array_replace函数。 array_merge函数会以给定的数组(按照正确的顺序)为起点,并使用来自实际数组的数据覆盖/添加键:

$customer['address']    = '123 fake st';
$customer['name']       = 'Tim';
$customer['dob']        = '12/08/1986';
$customer['dontSortMe'] = 'this value doesnt need to be sorted';

$properOrderedArray = array_merge(array_flip(array('name', 'dob', 'address')), $customer);
// or
$properOrderedArray = array_replace(array_flip(array('name', 'dob', 'address')), $customer);

// $properOrderedArray: array(
//   'name'       => 'Tim',
//   'dob'        => '12/08/1986',
//   'address'    => '123 fake st',
//   'dontSortMe' => 'this value doesnt need to be sorted')

PS:我回答这个“陈旧”的问题,因为我认为之前的所有循环都过于复杂。


36
如果你的键是字符串,它能很好地工作,但对于数字键则不是。PHP文档:「如果输入数组具有相同的字符串键,则该键的后续值将覆盖先前的值。然而,如果数组包含数字键,则后续值不会覆盖原始值,而是会被追加。」 - bolbol
8
好的,如果键在值中不存在怎么办?我需要这个,但只有在任何一个键存在时才需要... 可能需要对它进行foreach循环... - Solomon Closson
7
我的情况需要使用array_replace而不是array_merge。array_merge会将两个数组的值合并在一起,而不是将第二个数组替换为有序键中的值。 - neofreko
3
几年前在寻找其他东西时,我偶然发现了你的解决方案。当时我心想:相比循环,这非常高效。现在我需要用到你的解决方案,但我花了一个小时才找到它!谢谢! - Michael
5
此外,如果“order”数组(即array('name', 'dob', 'address'))的键数多于要排序的数组,则对结果排序后的数组进行额外的array_intersect操作与原始数组相交,以切掉在array_merge中添加的杂散键。 - bbe
显示剩余4条评论

121

给你:

function sortArrayByArray(array $array, array $orderArray) {
    $ordered = array();
    foreach ($orderArray as $key) {
        if (array_key_exists($key, $array)) {
            $ordered[$key] = $array[$key];
            unset($array[$key]);
        }
    }
    return $ordered + $array;
}

12
所以你可以用加号符号连接两个数组?我从不知道,我一直在使用 array_merge() - alex
5
使用这个比使用 usort() 或者 uasort() 更好吗? - grantwparks
5
一旦找到了值,你应该插入一个break语句。 - Adel
7
当你将array_merge()替换为数组+运算符时,请非常小心。它按键合并(包括数字键),从左到右 合并,而array_merge右到左 合并,并且从不覆盖数字键。例如,[0,1,2]+[0,5,6,7] = [0,1,2,7],而array_merge([0,1,2],[0,5,6,7])=[0,1,2,0,5,6,7],而['a' => 5] + ['a' => 7] = ['a' => 5]但是array_merge(['a' => 5], ['a' => 7]) = ['a' => 7] - flu
使用加号 + 安全吗? - crmpicco
1
根据PHP文档+或联合运算符在某些情况下是完全有效的,如果您必须保留数字键,则正是您想要的。 - Hexodus

59

这个解决方案怎么样?

$order = array(1,5,2,4,3,6);

$array = array(
    1 => 'one',
    2 => 'two',
    3 => 'three',
    4 => 'four',
    5 => 'five',
    6 => 'six'
);

uksort($array, function($key1, $key2) use ($order) {
    return (array_search($key1, $order) > array_search($key2, $order));
});

6
这不是很有效率。每次比较都需要在数组中进行两次线性搜索。如果我们假设 uksort() 的时间复杂度为 O(n * log n),那么这个算法的运行时间为 O(n^2 * log(n)) - TheOperator
这个解决方案对于缺少排序顺序值的数组非常有效。对于小型数组,复杂度可能可以忽略不计。使用多个数组函数的解决方案甚至可能更糟糕。 - 2ndkauboy
只需在“use”之前对$order进行“array_flip”,就可以使其更快。 - V.Volkov
@V.Volkov 但是在那种情况下,你会得到错误的结果。 - Peter de Groot
@PeterdeGroot 抱歉没有说明清楚 :) 在 uksort 之前 $order = array_flip( $order );,在 uksort 中使用 return $order[ $key1 ] > $order[ $key2 ]; - V.Volkov
喜欢这个解决方案。自 PHP 8 开始,应该切换到布尔比较(RFC for stable sorting https://wiki.php.net/rfc/stable_sorting),因此不要使用逻辑操作符“>”,而是使用算术操作符“-”。 - ETNyx

45

PHP >= 5.3.0 的另一种方法:

$customer['address'] = '123 fake st';
$customer['name'] = 'Tim';
$customer['dob'] = '12/08/1986';
$customer['dontSortMe'] = 'this value doesnt need to be sorted';

$customerSorted = array_replace(array_flip(array('name', 'dob', 'address')), $customer);

结果:

Array (
  [name] => Tim
  [dob] => 12/08/1986
  [address] => 123 fake st
  [dontSortMe] => this value doesnt need to be sorted
)

适用于字符串和数字键。


3
尽管它们都可以工作,但我发现array_replace()array_merge()更能传达意图。 - Jason McCreary
1
array_replace also leaves the variable type intact. If one of the values in your array would have been (string) '1' and you'd have used the + operator, the value would have been turned into (int) 1 - halfpastfour.am
1
这也适用于数字键(array_merge()只会将它们附加到末尾?)。逻辑在这里非常清楚地解释了。首先array_flip()将$order数组的值更改为键。其次array_replace()用第二个数组中具有相同键的值替换第一个数组的值。如果您需要根据另一个数组的键对数组进行排序,则甚至不必使用array_flip - ᴍᴇʜᴏᴠ

28
function sortArrayByArray(array $toSort, array $sortByValuesAsKeys)
{
    $commonKeysInOrder = array_intersect_key(array_flip($sortByValuesAsKeys), $toSort);
    $commonKeysWithValue = array_intersect_key($toSort, $commonKeysInOrder);
    $sorted = array_merge($commonKeysInOrder, $commonKeysWithValue);
    return $sorted;
}

17

我使用了Darkwaltz4的解决方案,但是使用了array_fill_keys,而不是array_flip,如果$array中没有设置键,则使用NULL填充。

$properOrderedArray = array_replace(array_fill_keys($keys, null), $array);

为了从 $array 中删除未列在 $keys 中的项目,我使用了以下代码:array_replace(array_fill_keys($keys, null), array_intersect_key($array, array_flip($keys))); - VladSavitsky
我不知道我何时点赞了这个,但我回来了,希望我能再次点赞 :D 与使用array_flip()的任何解决方案不同,当值不是标量时,此答案仍然有效。 - jessica

16

将一个数组作为您的订单:

$order = array('north', 'east', 'south', 'west');

您可以使用 array_intersect­Docs 根据值对另一个数组进行排序:


/* sort by value: */
$array = array('south', 'west', 'north');
$sorted = array_intersect($order, $array);
print_r($sorted);

在你的情况下,要按键排序,请使用array_intersect_key­Docs

/* sort by key: */
$array = array_flip($array);
$sorted = array_intersect_key(array_flip($order), $array);
print_r($sorted);

这两个函数将保留第一个参数的顺序,并且仅从第二个数组返回值(或键)。

因此,对于这两种标准情况,您不需要编写自己的函数来执行排序/重新排列。


1
交集将消除那些他事先不知道的条目。 - DanMan
1
这种方式不适用于按键排序。array_intersect_key 只会返回 array1 中的值,而不是 array2。 - spooky
同意pavsid的观点 - array_intersect_key示例是不正确的 - 它返回第一个数组中的值,而不是第二个数组。 - Jonathan Aquino

6

没有魔法...

$items=array(28=>c,4=>b,5=>a);
$seq=array(5,4,28);    
SortByKeyList($items,$seq) result: array(5=>a,4=>b,28=>c);

function sortByKeyList($items,$seq){
    $ret=array();
    if(empty($items) || empty($seq)) return false;
    foreach($seq as $key){$ret[$key]=$items[$key];}
    return $ret;
}

4
好的,谢谢。只需更新$dataset以匹配参数名称即可,这个功能效果很不错。 - kursus
1
这个答案没有任何魔法,没有解释,没有定义的常量,也没有遵循PSR-12编码指南,并且有条件地返回false而不是数组。 - mickmackusa

3

如果您的数组中还有数组,您需要稍微调整Eran函数...

function sortArrayByArray($array,$orderArray) {
    $ordered = array();
    foreach($orderArray as $key => $value) {
        if(array_key_exists($key,$array)) {
                $ordered[$key] = $array[$key];
                unset($array[$key]);
        }
    }
    return $ordered + $array;
}

3

PHP有一些函数能够帮助你实现这个功能:

$arrayToBeSorted = array('west', 'east', 'south', 'north');
$order = array('north', 'south', 'east', 'west');

// sort array
usort($arrayToBeSorted, function($a, $b) use ($order){
    // sort using the numeric index of the second array
    $valA = array_search($a, $order);
    $valB = array_search($b, $order);

    // move items that don't match to end
    if ($valA === false)
        return -1;
    if ($valB === false)
        return 0;

    if ($valA > $valB)
        return 1;
    if ($valA < $valB)
        return -1;
    return 0;
});

Usort函数可以帮助你完成所有的工作,而array_search函数则提供了键值。当array_search()无法找到匹配项时,它会返回false,因此不在排序数组中的项目自然会移动到数组底部。

注意:uasort()函数将对数组进行排序,但不影响键值关系。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接