【问题标题】:Sorting Arrays of objects by predefined map of values按预定义的值映射对对象数组进行排序
【发布时间】:2019-03-13 19:51:25
【问题描述】:

我有以下数组:

$inputArray = Array(
    [0] => stdClass Object (
        [id] => 8
    )

    [1] => stdClass Object (
        [id] => 7
    )

    [2] => stdClass Object (
        [id] => 5
    )
)

$sortingArray = [5,8,1]

我正在寻找一种通过 id “值映射”对输入数组进行排序的有效方法。预期的输出应该是 $inputArray 重新排序,以便第 3 项是第一项,第一项是第 2 项,依此类推。

谢谢!

【问题讨论】:

  • 试试usort()..
  • usor 就我所见是比较数组中的 2 个值,而不是与值的外部映射...或者至少我找不到一个
  • 你能发布你到目前为止所做的事情吗?
  • 到目前为止,我已经迭代了对象数组并将每个对象与列表进行比较。如果发现我插入一个新数组并从 $inputArray 中取消设置值,但我相信这是次优的,必须是更好的方法..

标签: php arrays sorting multidimensional-array stack


【解决方案1】:

您可以使用array_flip 创建一个临时数组。这将翻转数组意味着值将是关键。

使用usort 对数组进行排序。

$tempArr = array_flip( $sortingArray  );
usort($inputArray, function($a, $b) use ( $tempArr ) {
    $tempA = isset( $tempArr[ $a->id ] ) ? $tempArr[ $a->id ] : 999999; //If id does not exist on $sortingArray. Use 999999 as an index
    $tempB = isset( $tempArr[ $b->id ] ) ? $tempArr[ $b->id ] : 999999;
    return $tempA - $tempB;
});

echo "<pre>";
print_r( $inputArray );
echo "</pre>";

这将导致:

Array
(
    [0] => stdClass Object
        (
            [id] => 5
        )

    [1] => stdClass Object
        (
            [id] => 8
        )

    [2] => stdClass Object
        (
            [id] => 7
        )

)

【讨论】:

    【解决方案2】:

    我在这里使用了一些东西,主要是我使用排序数组作为 array_replace() 的目标,并使用 array_column() 来索引对象(这需要 PHP 7.0+ 才能使用对象)...

    $input = array_column($inputArray, null, "id");   
    $sort = array_fill_keys($sortingArray, null);
    $output = array_filter(array_replace($sort, $input));
    

    array_filter() 将删除不在输入数组中但在排序数组中的所有元素。使用array_fill_keys() 代替array_flip(),这样我就可以设置一个空值,从而允许过滤器工作。

    【讨论】:

    • 我认为这是一种更优雅的方法。奇迹般有效! *我认为它是首选方法的原因是它过滤了原始输入数组中的重复项,并且如果键不存在则不会失败
    • 我不认为0 将是id 的有效值,但如果将来有人使用这种方法,array_filter() 将使用null 值销毁它们.
    【解决方案3】:

    首先,如果您的输入数组是 MySQL 查询的结果集,您应该通过 ORDER BY 子句对数据进行排序。 FIELD() 是一个出色的工作工具,有点奇怪,您需要反转排序逻辑并在函数后写 DESC

    资源:https://www.electrictoolbox.com/mysql-order-specific-field-values/(向下滚动到“gotcha”)

    SQL 小提琴演示:http://sqlfiddle.com/#!9/6b996f/1


    除此之外,我将提供 Nigel 和 Eddie 的解决方案的两个改进版本,它们利用了一些 php7+ 的优点。您选择实施哪一项将与:

    1. 是否要允许具有重复 id 值的元素“存活”该过程
    2. 对于您的实际项目数据(输入数组和自定义订单数组的大小),哪个执行效率更高
    3. 个人喜好(sn-ps 都可以合理地写成 3 到 5 行,所以简洁不是标准)

    解决方案 #1:usort()null coalescing operator

    PHP Demo

    $array = [
        (object)['id' => 8],
        (object)['id' => 7],
        (object)['id' => 5]
    ];
    
    $order = [5, 8, 1];  // custom sort order
    
    $order = array_flip($order);  // restructure for easy lookup with isset()
    $order[''] = max(array_column($array, 'id')) + 1;  // append value higher than max for outlying ids
    
    usort($array, function($a, $b) use ($order) {
        return ($order[$a->id] ?? $order['']) <=> ($order[$b->id] ?? $order['']);
    });
    var_export($array);
    

    *请注意,我本可以将 $outlier 作为第二个 use 参数传递到自定义函数范围中,并将所有 $order[''] 变量替换为 $outlier,但我选择将异常数据附加到查找中数组。

    *我在自定义函数中使用括号不仅是为了提高可读性,而且是为了保证评估按预期执行——我实际上并没有费心检查评估是否与括号分组相同。

    解决方案 #2:array_replace() 带有键控数组的翻转和过滤排序数组

    (PHP Demo)

    $array = [
        (object)['id' => 8],
        (object)['id' => 7],
        (object)['id' => 5]
    ];
    
    $order = [5, 8, 1];
    
    $order = array_flip($order);                            // flip for future key comparisons
    $keyed = array_column($array, null, 'id');              // declare id values as new keys
    $filtered_order = array_intersect_key($order, $keyed);  // remove unwanted order keys
    $replaced = array_replace($filtered_order, $keyed);     // apply objects to output array
    var_export(array_values($replaced));                    // re-index the output (if desired)
    

    *请注意,这不会生成一个过大的 null 值数组,也不会删除“错误”的值。

    *我再次声明,如果输入数组中有重复的id 值,使用array_column() 会损坏数据。

    【讨论】:

    • 谢谢,关于 SQL 不幸的是……关于 array_search 的重构版本,第一个响应更短且更有效……不是吗?
    • 效率很大程度上取决于所涉及的数组的大小。如果您的意思是重复页面上的usort() - array_search() 答案,我没有针对它们进行测试,但是array_search()isset() 更努力。使用isset() 的成本是一次调用array_flip() 来设置它。 array_search() 实际上会遍历数组,而breaks 在找到匹配项时会遍历。 isset() 不会这样做——这是 php 处理哈希映射的神奇之处。我没有资格详细解释这一点。
    猜你喜欢
    • 1970-01-01
    • 2018-01-15
    • 2018-05-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-15
    相关资源
    最近更新 更多