【问题标题】:Sorting Array of Std Class Objects by Custom Order (PHP)按自定义顺序对标准类对象数组进行排序 (PHP)
【发布时间】:2018-02-14 04:40:24
【问题描述】:

我有一组包含不同汽车类型(例如紧凑型汽车、中型汽车等)的 StdClass 对象。该数组应按以下顺序排序:

  • 经济型车
  • 小型车
  • 中型车
  • 标准车
  • 全尺寸车
  • 高级轿车
  • 豪华轿车
  • 标准尺寸敞篷车
  • 中型SUV
  • 标准尺寸 SUV
  • 全尺寸SUV
  • 小型货车
  • 阵列中剩余的汽车应按价格从低到高排序(例如:在小型货车之后,显示特种车、紧凑型 SUV、全尺寸混合动力车、运动车等...)。

我在按价格对剩余汽车进行分类时遇到问题。我先尝试按价格排序,然后在第二个 usort 中排序,但它没有按预期工作。任何帮助,将不胜感激。我之前发布了一个类似的问题,但我想我会重新发布一个更有条理的需求版本。

输入数组:

array (
  0 => 
  stdClass::__set_state(array(
     'description' => 'Mid-Size SUV',
     'codes' => 
    array (
      0 => 'IFAR',
    ),
     'max_people' => '5',
     'min_people' => '5',
     'max_bags' => '4',
     'min_bags' => '1',
     'price' => '30.31',
  )),
  1 => 
  stdClass::__set_state(array(
     'description' => 'Standard-Size SUV',
     'codes' => 
    array (
      0 => 'SFAR',
      1 => 'RFAR',
    ),
     'max_people' => '7',
     'min_people' => '5',
     'max_bags' => '4',
     'min_bags' => '1',
     'price' => '35.53',
  )),
  2 => 
  stdClass::__set_state(array(
     'description' => 'Economy Car',
     'codes' => 
    array (
      0 => 'ECAR',
      1 => 'EDAR',
    ),
     'max_people' => '5',
     'min_people' => '4',
     'max_bags' => '2',
     'min_bags' => '1',
     'price' => '37.21',
  )),
  3 => 
  stdClass::__set_state(array(
     'description' => 'Specialty Vehicle',
     'codes' => 
    array (
      0 => 'XXAR',
    ),
     'max_people' => false,
     'min_people' => false,
     'max_bags' => '',
     'min_bags' => '',
     'price' => '36.72',
  )),
  4 => 
  stdClass::__set_state(array(
     'description' => 'Compact Car',
     'codes' => 
    array (
      0 => 'CCAR',
      1 => 'CDAR',
    ),
     'max_people' => '5',
     'min_people' => '4',
     'max_bags' => '3',
     'min_bags' => '1',
     'price' => '37.21',
  )),
  5 => 
  stdClass::__set_state(array(
     'description' => 'Mid-Size Car',
     'codes' => 
    array (
      0 => 'ICAR',
      1 => 'IDAR',
    ),
     'max_people' => '5',
     'min_people' => '5',
     'max_bags' => '4',
     'min_bags' => '1',
     'price' => '39.46',
  )),
  6 => 
  stdClass::__set_state(array(
     'description' => 'Standard-Size Car',
     'codes' => 
    array (
      0 => 'SCAR',
      1 => 'SDAR',
    ),
     'max_people' => '5',
     'min_people' => '5',
     'max_bags' => '4',
     'min_bags' => '1',
     'price' => '41.77',
  )),
  7 => 
  stdClass::__set_state(array(
     'description' => 'Minivan',
     'codes' => 
    array (
      0 => 'MVAR',
    ),
     'max_people' => '7',
     'min_people' => '7',
     'max_bags' => '5',
     'min_bags' => '2',
     'price' => '43.18',
  )),
  8 => 
  stdClass::__set_state(array(
     'description' => 'Full-Size Car',
     'codes' => 
    array (
      0 => 'FCAR',
      1 => 'FDAR',
    ),
     'max_people' => '5',
     'min_people' => '5',
     'max_bags' => '4',
     'min_bags' => '1',
     'price' => '43.50',
  )),
  9 => 
  stdClass::__set_state(array(
     'description' => 'Compact SUV',
     'codes' => 
    array (
      0 => 'CFAR',
    ),
     'max_people' => '5',
     'min_people' => '5',
     'max_bags' => '3',
     'min_bags' => '3',
     'price' => '46.42',
  )),
  10 => 
  stdClass::__set_state(array(
     'description' => 'Full-Size Hybrid',
     'codes' => 
    array (
      0 => 'FCAH',
    ),
     'max_people' => false,
     'min_people' => false,
     'max_bags' => '',
     'min_bags' => '',
     'price' => '48.00',
  )),
  11 => 
  stdClass::__set_state(array(
     'description' => 'Standard-Size Convertible',
     'codes' => 
    array (
      0 => 'STAR',
    ),
     'max_people' => '4',
     'min_people' => '4',
     'max_bags' => '4',
     'min_bags' => '1',
     'price' => '48.39',
  )),
  12 => 
  stdClass::__set_state(array(
     'description' => 'Premium Car',
     'codes' => 
    array (
      0 => 'PCAR',
    ),
     'max_people' => '5',
     'min_people' => '5',
     'max_bags' => '4',
     'min_bags' => '2',
     'price' => '48.72',
  )),
  13 => 
  stdClass::__set_state(array(
     'description' => 'Sports Car',
     'codes' => 
    array (
      0 => 'XSAR',
      1 => 'SSAR',
    ),
     'max_people' => '5',
     'min_people' => '4',
     'max_bags' => '3',
     'min_bags' => '1',
     'price' => '48.72',
  )),
  14 => 
  stdClass::__set_state(array(
     'description' => 'Luxury Car',
     'codes' => 
    array (
      0 => 'LCAR',
      1 => 'LDAR',
    ),
     'max_people' => '5',
     'min_people' => '5',
     'max_bags' => '5',
     'min_bags' => '1',
     'price' => '52.16',
  )),
  15 => 
  stdClass::__set_state(array(
     'description' => 'Full-Size SUV',
     'codes' => 
    array (
      0 => 'FFAR',
    ),
     'max_people' => '8',
     'min_people' => '7',
     'max_bags' => '4',
     'min_bags' => '2',
     'price' => '52.92',
  )),
  16 => 
  stdClass::__set_state(array(
     'description' => 'Compact Convertible',
     'codes' => 
    array (
      0 => 'CTAR',
    ),
     'max_people' => '4',
     'min_people' => '4',
     'max_bags' => '1',
     'min_bags' => '1',
     'price' => '91.17',
  )),
  17 => 
  stdClass::__set_state(array(
     'description' => 'Premium SUV',
     'codes' => 
    array (
      0 => 'PFAR',
      1 => 'UFAR',
    ),
     'max_people' => '7',
     'min_people' => '5',
     'max_bags' => '5',
     'min_bags' => '1',
     'price' => '92.64',
  )),
  18 => 
  stdClass::__set_state(array(
     'description' => 'Specialty Car',
     'codes' => 
    array (
      0 => 'XCAR',
    ),
     'max_people' => '5',
     'min_people' => '5',
     'max_bags' => '3',
     'min_bags' => '3',
     'price' => '94.42',
  )),
  19 => 
  stdClass::__set_state(array(
     'description' => 'Mid-Size Convertible',
     'codes' => 
    array (
      0 => 'ITAR',
    ),
     'max_people' => '4',
     'min_people' => '4',
     'max_bags' => '2',
     'min_bags' => '2',
     'price' => '97.98',
  )),
  20 => 
  stdClass::__set_state(array(
     'description' => 'Full-Size Van',
     'codes' => 
    array (
      0 => 'FVAR',
    ),
     'max_people' => '15',
     'min_people' => '12',
     'max_bags' => '5',
     'min_bags' => '1',
     'price' => '90.38',
  )),
  21 => 
  stdClass::__set_state(array(
     'description' => 'Luxury SUV',
     'codes' => 
    array (
      0 => 'LFAR',
    ),
     'max_people' => '5',
     'min_people' => '5',
     'max_bags' => '3',
     'min_bags' => '3',
     'price' => '113.66',
  )),
  22 => 
  stdClass::__set_state(array(
     'description' => 'Premium Convertible',
     'codes' => 
    array (
      0 => 'PTAR',
    ),
     'max_people' => '4',
     'min_people' => '4',
     'max_bags' => '1',
     'min_bags' => '1',
     'price' => '121.15',
  )),
  23 => 
  stdClass::__set_state(array(
     'description' => 'Luxury Convertible',
     'codes' => 
    array (
      0 => 'LTAR',
    ),
     'max_people' => '4',
     'min_people' => '4',
     'max_bags' => '1',
     'min_bags' => '1',
     'price' => '129.01',
  )),
  24 => 
  stdClass::__set_state(array(
     'description' => 'Premium Van',
     'codes' => 
    array (
      0 => 'PVAR',
    ),
     'max_people' => '15',
     'min_people' => '15',
     'max_bags' => '1',
     'min_bags' => '1',
     'price' => '159.49',
  )),
  25 => 
  stdClass::__set_state(array(
     'description' => 'Specialty Convertible',
     'codes' => 
    array (
      0 => 'XTAR',
    ),
     'max_people' => '4',
     'min_people' => '4',
     'max_bags' => '2',
     'min_bags' => '2',
     'price' => '307.64',
  )),
)

代码:

    $order = [
        'Economy Car' => 1,
        'Compact Car' => 2,
        'Mid-Size Car' => 3,
        'Standard-Size Car' => 4,
        'Full-Size Car' => 5,
        'Premium Car' => 6,
        'Luxury Car' => 7,
        'Standard-Size Convertible' => 8,
        'Mid-Size SUV' => 9,
        'Standard SUV' => 10,
        'Full-Size SUV' => 11,
        'Minivan' => 12
    ];

    usort($car_types,function($a, $b) {
        return $a->price == $b->price ? 0 : $a->price > $b->price;
    });

    usort($car_types, function($a, $b) use ($order) {
        $a_set = intval(isset($order[$a->description]));
        $b_set = intval(isset($order[$b->description]));

        return $a_set === $b_set && $a_set === 1
            ? $order[$a->description] - $order[$b->description]
            : $b_set - $a_set;
    });

新输出:

Array
(
[0] => stdClass Object
    (
        [description] => Economy Car
        [codes] => Array
            (
                [0] => ECAR
                [1] => EDAR
            )

        [max_people] => 5
        [min_people] => 4
        [max_bags] => 2
        [min_bags] => 1
        [price] => 37.21
    )

[1] => stdClass Object
    (
        [description] => Compact Car
        [codes] => Array
            (
                [0] => CCAR
                [1] => CDAR
            )

        [max_people] => 5
        [min_people] => 4
        [max_bags] => 3
        [min_bags] => 1
        [price] => 37.21
    )

[2] => stdClass Object
    (
        [description] => Mid-Size Car
        [codes] => Array
            (
                [0] => ICAR
                [1] => IDAR
            )

        [max_people] => 5
        [min_people] => 5
        [max_bags] => 4
        [min_bags] => 1
        [price] => 39.46
    )

[3] => stdClass Object
    (
        [description] => Standard-Size Car
        [codes] => Array
            (
                [0] => SCAR
                [1] => SDAR
            )

        [max_people] => 5
        [min_people] => 5
        [max_bags] => 4
        [min_bags] => 1
        [price] => 41.77
    )

[4] => stdClass Object
    (
        [description] => Full-Size Car
        [codes] => Array
            (
                [0] => FCAR
                [1] => FDAR
            )

        [max_people] => 5
        [min_people] => 5
        [max_bags] => 4
        [min_bags] => 1
        [price] => 43.50
    )

[5] => stdClass Object
    (
        [description] => Premium Car
        [codes] => Array
            (
                [0] => PCAR
            )

        [max_people] => 5
        [min_people] => 5
        [max_bags] => 4
        [min_bags] => 2
        [price] => 48.72
    )

[6] => stdClass Object
    (
        [description] => Luxury Car
        [codes] => Array
            (
                [0] => LCAR
                [1] => LDAR
            )

        [max_people] => 5
        [min_people] => 5
        [max_bags] => 5
        [min_bags] => 1
        [price] => 52.16
    )

[7] => stdClass Object
    (
        [description] => Standard-Size Convertible
        [codes] => Array
            (
                [0] => STAR
            )

        [max_people] => 4
        [min_people] => 4
        [max_bags] => 4
        [min_bags] => 1
        [price] => 48.39
    )

[8] => stdClass Object
    (
        [description] => Mid-Size SUV
        [codes] => Array
            (
                [0] => IFAR
            )

        [max_people] => 5
        [min_people] => 5
        [max_bags] => 4
        [min_bags] => 1
        [price] => 30.31
    )

[9] => stdClass Object
    (
        [description] => Standard-Size SUV
        [codes] => Array
            (
                [0] => SFAR
                [1] => RFAR
            )

        [max_people] => 7
        [min_people] => 5
        [max_bags] => 4
        [min_bags] => 1
        [price] => 35.53
    )

[10] => stdClass Object
    (
        [description] => Full-Size SUV
        [codes] => Array
            (
                [0] => FFAR
            )

        [max_people] => 8
        [min_people] => 7
        [max_bags] => 4
        [min_bags] => 2
        [price] => 52.92
    )

[11] => stdClass Object
    (
        [description] => Minivan
        [codes] => Array
            (
                [0] => MVAR
            )

        [max_people] => 7
        [min_people] => 7
        [max_bags] => 5
        [min_bags] => 2
        [price] => 43.18
    )

[12] => stdClass Object
    (
        [description] => Specialty Vehicle
        [codes] => Array
            (
                [0] => XXAR
            )

        [max_people] => 
        [min_people] => 
        [max_bags] => 
        [min_bags] => 
        [price] => 36.72
    )

[13] => stdClass Object
    (
        [description] => Compact SUV
        [codes] => Array
            (
                [0] => CFAR
            )

        [max_people] => 5
        [min_people] => 5
        [max_bags] => 3
        [min_bags] => 3
        [price] => 46.42
    )

[14] => stdClass Object
    (
        [description] => Sports Car
        [codes] => Array
            (
                [0] => XSAR
                [1] => SSAR
            )

        [max_people] => 5
        [min_people] => 4
        [max_bags] => 3
        [min_bags] => 1
        [price] => 48.72
    )

[15] => stdClass Object
    (
        [description] => Full-Size Hybrid
        [codes] => Array
            (
                [0] => FCAH
            )

        [max_people] => 
        [min_people] => 
        [max_bags] => 
        [min_bags] => 
        [price] => 48.00
    )

[16] => stdClass Object
    (
        [description] => Full-Size Van
        [codes] => Array
            (
                [0] => FVAR
            )

        [max_people] => 15
        [min_people] => 12
        [max_bags] => 5
        [min_bags] => 1
        [price] => 90.38
    )

[17] => stdClass Object
    (
        [description] => Compact Convertible
        [codes] => Array
            (
                [0] => CTAR
            )

        [max_people] => 4
        [min_people] => 4
        [max_bags] => 1
        [min_bags] => 1
        [price] => 91.17
    )

[18] => stdClass Object
    (
        [description] => Premium SUV
        [codes] => Array
            (
                [0] => PFAR
                [1] => UFAR
            )

        [max_people] => 7
        [min_people] => 5
        [max_bags] => 5
        [min_bags] => 1
        [price] => 92.64
    )

[19] => stdClass Object
    (
        [description] => Specialty Car
        [codes] => Array
            (
                [0] => XCAR
            )

        [max_people] => 5
        [min_people] => 5
        [max_bags] => 3
        [min_bags] => 3
        [price] => 94.42
    )

[20] => stdClass Object
    (
        [description] => Mid-Size Convertible
        [codes] => Array
            (
                [0] => ITAR
            )

        [max_people] => 4
        [min_people] => 4
        [max_bags] => 2
        [min_bags] => 2
        [price] => 97.98
    )

[21] => stdClass Object
    (
        [description] => Luxury SUV
        [codes] => Array
            (
                [0] => LFAR
            )

        [max_people] => 5
        [min_people] => 5
        [max_bags] => 3
        [min_bags] => 3
        [price] => 113.66
    )

[22] => stdClass Object
    (
        [description] => Premium Convertible
        [codes] => Array
            (
                [0] => PTAR
            )

        [max_people] => 4
        [min_people] => 4
        [max_bags] => 1
        [min_bags] => 1
        [price] => 121.15
    )

[23] => stdClass Object
    (
        [description] => Luxury Convertible
        [codes] => Array
            (
                [0] => LTAR
            )

        [max_people] => 4
        [min_people] => 4
        [max_bags] => 1
        [min_bags] => 1
        [price] => 129.01
    )

[24] => stdClass Object
    (
        [description] => Premium Van
        [codes] => Array
            (
                [0] => PVAR
            )

        [max_people] => 15
        [min_people] => 15
        [max_bags] => 1
        [min_bags] => 1
        [price] => 159.49
    )

[25] => stdClass Object
    (
        [description] => Specialty Convertible
        [codes] => Array
            (
                [0] => XTAR
            )

        [max_people] => 4
        [min_people] => 4
        [max_bags] => 2
        [min_bags] => 2
        [price] => 307.64
    )
)

【问题讨论】:

  • order收集的物品应该从原始数组中删除,然后将剩余的物品按价格排序。此时你有两个数组,这两个数组应该合并。
  • 这是数据库查询的结果吗?如果是这样,至少在 MySQL 中您可以使用ORDER BY FIELD(field_name, 'VAl_1', 'VAL_2'),这通常比在 PHP 中更有效。我认为也应该可以按价格对其余部分进行排序。
  • 另外,如果你确实必须在 PHP 而不是 SQL 中这样做,根据我的经验,像 Laravel 的 collections 或 Knapsack 这样的集合库通常会让这样的事情变得不那么痛苦,因为它可以让你链接这些方法比原生数组更容易。与往常一样,YMMV,如果 OP 不想要额外的开销,我会理解的。
  • 嘿@MatthewDaly 这个信息来自一个API,所以很遗憾没有使用数据库来存储这个信息

标签: php arrays sorting multidimensional-array stdclass


【解决方案1】:

您只需拨打usort 即可完成此操作。

首先,您还可以使用array_flip 来更轻松地管理$order 数组。这样您就不必输入(或更改)数值:

$order = array_flip([
    'Economy Car',
    'Compact Car',
    'Mid-Size Car',
    'Standard-Size Car',
    'Full-Size Car',
    'Premium Car',
    'Luxury Car',
    'Standard-Size Convertible',
    'Mid-Size SUV',
    'Standard SUV',
    'Full-Size SUV',
    'Minivan'
]);

还有排序本身:

usort($car_types, function($a, $b) use ($order) {
    $ret =  (isset($order[$a->description]) ? $order[$a->description] : count($order))
          - (isset($order[$b->description]) ? $order[$b->description] : count($order));
    return $ret ? $ret : ($a->price - $b->price) * 100;
});

第一个赋值根据description字段按顺序计算差值。如果不知道顺序,则采用默认值count($order)(使用三元运算符):这样,不在$order 数组中的汽车类型将在@987654330 中具有条目的任何汽车类型之后排序@。 count 函数在这里工作,假设您已经对以 0 开头的类型进行编号(array_flip 就是这种情况),但您可以使用任何足够大的数字来代替。

return 语句然后检查上述值是否不为零,如果是则返回它。如果为零,则返回价格差异(以美分为单位)。采用以美分为单位的值是因为返回值应为整数,而不是浮点数。正如documentation中所说:

警告:从比较函数返回非整数值,例如float,将导致回调返回值的内部强制转换为整数。因此,诸如 0.99 和 0.1 之类的值都将被转换为整数值 0,这将比较这些值是否相等。

【讨论】:

  • 这似乎工作了 99.5%...只有一辆车似乎没有正确分类。见:prntscr.com/ghj2hu ...想法?
  • 更正:排序回调返回的差值必须至少为1(或不大于-1),否则将被视为0。
  • 我在回答中添加了解释。
  • 虽然我们的答案在功能上是相同的,但我不喜欢硬编码任意“最大值”值,因为$order 数组可能会随着时间的推移而变化,而代码保持不变。
  • 好吧,公平地说,PHP_INT_MAX 也是硬编码。无论如何,虽然我发现这是一个非常理论化的评论(999 比实际数量的汽车类型要高得多),但我已经根据 $order 数组的大小将其更新为动态的。
【解决方案2】:

所以只需在$order 中指定任何的最大分类重量,然后如果汽车具有相同的重量,则添加二次价格比较。这种方法要简单得多,只需要一次排序操作。

usort($car_types, function($a, $b) use ($order) {
    $a_weight = key_exists($a->description, $order) ? $order[$a->description] : PHP_INT_MAX;
    $b_weight = key_exists($b->description, $order) ? $order[$b->description] : PHP_INT_MAX;

    if( $a_weight != $b_weight ) {
        return $a_weight - $b_weight;
    } else {
        return ( $a->price - $b->price ) * 100;
    }
});

【讨论】:

  • 谢谢!我还应该为此使用这两种选择吗?先有定价的那个,然后是你提供的那个?
  • 没有。正如我所说,这可以一次性完成这两项任务。
  • 这似乎工作了 99.5%...只有一辆车似乎没有正确分类。见:prntscr.com/ghj2hu ...想法?
  • 啊,价格中有美分。 PHP 在转换为整数时不会四舍五入,它只是去掉小数部分。如果你将价格乘以 100,你应该得到你想要的结果。 [更新答案]
  • 值得注意的是,如果您实际上是在处理真实货币的交换,则永远不应该使用浮点数来存储一定数量的货币。 moneyphp/money 之类的东西会安全准确地处理它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-06-24
  • 1970-01-01
  • 2014-04-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多