简体   繁体   English

如何通过多个键,字符串和整数的值对多维数组进行排序

[英]How to Sort Multidimensional Array by Values of Multiple Keys, Strings and Integers

I have an array of people (lawmakers) listing their level, state, chamber and district. 我有很多人(立法者)列出他们的级别,州,会议厅和地区。 What I'd like is to sort the array based on the values of these 4 keys, but with a custom order since some of these values are strings. 我想要的是根据这四个键的值对数组进行排序,但是要按自定义顺序排序,因为其中一些值是字符串。 For example, I'd want to first sort by "level" with the order of ("federal","state", then anything else), then state in alphabetical order ascending, then chamber by ("governor","upper","lower", then anything else), then district in number order ascending. 例如,我想先按“级别”以(“联邦”,“州”,然后是其他任何地方)的顺序排序,然后以字母顺序升序,然后按“(州长”,“上位”) ,“低”,“其他”),然后按编号顺序升序排列。 Here's a shortened example of the array I'm using. 这是我正在使用的数组的简化示例。

Array
(
[0] => stdClass Object
    (
        [district] => 15
        [state] => fl
        [level] => state
        [chamber] => upper

    )

[1] => stdClass Object
    (
        [district] => 50
        [state] => fl
        [level] => state
        [chamber] => lower
    )
[2] => stdClass Object
    (
        [district] => 0
        [state] => fl
        [level] => state
        [chamber] => governor

    )
)

@BusyBeaver's solution cmp() / compare() function is quite complex and difficult to update if you want to change order or, God forbid, add another field! @BusyBeaver的解决方案cmp() / compare()函数非常复杂,如果您想更改顺序,或者很难添加其他字段,则很难更新!

this was what I came up with: 这就是我想出的:

<?php
function sortValue($key, $value)
{
    static $arrValueOrder = [
        'level' => ['federal', 'state'],
        'chamber' => ['governor', 'upper', 'lower']
    ];

    if (array_key_exists($key, $arrValueOrder)) {
        if (($result = array_search($value, $arrValueOrder[$key])) === false) {
            $result = count($arrValueOrder[$key]);
        }
    } else {
        $result = $value;
    }
    return $result;
}

function cmp($a, $b)
{
    static $arrFieldOrder = ['level', 'state', 'chamber', 'district'];

    $result = 0;
    reset($arrFieldOrder);
    while ($result === 0 && list($key, $value) = each($arrFieldOrder)) {
        $aSortValue = sortValue($value, $a->$value);
        $bSortValue = sortValue($value, $b->$value);
        if ($aSortValue > $bSortValue) {
            $result = 1;
        } elseif ($aSortValue < $bSortValue) {
            $result = -1;
        }
    }
    return $result;
}

// setup some test data
$arrData = [
    (object)['district' => 15, 'state' => 'fl', 'level' => 'state', 'chamber' => 'upper'],
    (object)['district' => 15, 'state' => 'tx', 'level' => 'federal', 'chamber' => 'lower'],
    (object)['district' => 50, 'state' => 'fl', 'level' => 'state', 'chamber' => 'lower'],
    (object)['district' => 15, 'state' => 'tx', 'level' => 'federal', 'chamber' => 'upper'],
    (object)['district' => 0, 'state' => 'fl', 'level' => 'state', 'chamber' => 'governor'],
    (object)['district' => 15, 'state' => 'tx', 'level' => 'federal', 'chamber' => 'governor'],
    (object)['district' => 15, 'state' => 'tx', 'level' => 'federal', 'chamber' => 'foo'],
    (object)['district' => 15, 'state' => 'fl', 'level' => 'federal', 'chamber' => 'upper']
];

// sort it
usort($arrData, 'cmp');

// dump the result
var_dump($arrData);

usort() can solve your problem, you only have to write a good compare function that works as desired. usort()可以解决您的问题,您只需要编写一个按需工作的良好比较功能即可。

<?php

$lawmakers = ... // Your lawmakers array.
usort($lawmakers, "cmp");

// returns 0 if equal; -1 if $a < $b; 1 otherwise
function cmp($a, $b) {
    // 1. Compare level: "federal" < "state" < anything else
    if ($a["level"] === "federal" && $b["level"] !== "federal") return -1;
    else if ($a["level"] !== "federal" && $b["level"] === "federal") return 1;
    else if ($a["level"] === "state" && $b["level"] !== "state") return -1;
    else if ($a["level"] !== "state" && $b["level"] === "state") return 1;
    // 2. Compare state: alphabetical order
    if (strcmp($a["state"], $b["state"]) > 0) return 1;
    else if (strcmp($a["state"], $b["state"]) < 0) return -1;
    // 3. Compare chamber: "governor" < "upper" < "lower" < anything else
    if ($a["chamber"] === "governor" && $b["chamber"] !== "governor") return -1;
    else if ($a["chamber"] !== "governor" && $b["chamber"] === "governor") return 1;
    else if ($a["chamber"] === "upper" && $b["chamber"] !== "upper") return -1;
    else if ($a["chamber"] !== "upper" && $b["chamber"] === "upper") return 1;
    else if ($a["chamber"] === "lower" && $b["chamber"] !== "lower") return -1;
    else if ($a["chamber"] !== "lower" && $b["chamber"] === "lower") return 1;
    // 4. Compare district: ascending
    if ($a["district"] < $b["district"]) return -1;
    else if ($a["district"] > $b["district"]) return 1;
    // 5. If we came so far... $a and $b are equals
    return 0;
}
?>

You could also think about an Object-oriented solution (which still needs a compare function...) 您还可以考虑使用面向对象的解决方案(仍然需要比较功能...)

<?php

// Testing
$a = new Lawmaker("federal", "fl", "upper", 15);
$b = new Lawmaker("state", "fl", "lower", 7);
$c = new Lawmaker("federal", "fl", "upper", 8);
$d = new Lawmaker("other", "wy", "governor", 1);
$lawmakers = Array($a, $b, $c, $d);
mysort($lawmakers);
print_r($lawmakers);



// Class Lawmaker to define Lawmaker object
class Lawmaker
{
    // Properties declaration
    public $level;
    public $state;
    public $chamber;
    public $district;

    // Lawmaker constructor
    function __construct($level, $state, $chamber, $district) {
        $this->level = $level;
        $this->state = $state;
        $this->chamber = $chamber;
        $this->district = $district;
    }

    // Compare $this Lawmaker with $other Lawmaker
    // returns 0 if equal; -1 if $this < $other; 1 otherwise
    public function compare($other) {
        // 1. Compare level: "federal" < "state" < anything else
        if ($this->level === "federal" && $other->level !== "federal") return -1;
        else if ($this->level !== "federal" && $other->level === "federal") return 1;
        else if ($this->level === "state" && $other->level !== "state") return -1;
        else if ($this->level !== "state" && $other->level === "state") return 1;
        // 2. Compare state: alphabetical order
        if (strcmp($this->state, $other->state) > 0) return 1;
        else if (strcmp($this->state, $other->state) < 0) return -1;
        // 3. Compare chamber: "governor" < "upper" < "lower" < anything else
        if ($this->chamber === "governor" && $other->chamber !== "governor") return -1;
        else if ($this->chamber !== "governor" && $other->chamber === "governor") return 1;
        else if ($this->chamber === "upper" && $other->chamber !== "upper") return -1;
        else if ($this->chamber !== "upper" && $other->chamber === "upper") return 1;
        else if ($this->chamber === "lower" && $other->chamber !== "lower") return -1;
        else if ($this->chamber !== "lower" && $other->chamber === "lower") return 1;
        // 4. Compare district: ascending
        if ($this->district < $other->district) return -1;
        else if ($this->district > $other->district) return 1;
        // 5. If we came so far... $this and $other are equals
        return 0;
    }
}

function mysort(&$array) {
  // Simple selection sort. You can implement any sorting algorithm.
  for ($i = 0; $i < count($array); $i++) {
    $pos = $i;
    // Search for minimum
    for ($j = $i+1; $j < count($array); $j++) {
      if ($array[$j]->compare($array[$pos]) < 0) {
        $pos = $j;
      }
    }
    // Swap
    $temp = $array[$i];
    $array[$i] = $array[$pos];
    $array[$pos] = $temp;
  }
  // Done.
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM