为了账号安全,请及时绑定邮箱和手机立即绑定

PHP自定义排序:根据指定键手动排序数组

PHP自定义排序:根据指定键手动排序数组

PHP
森栏 2023-04-21 16:33:32
我有一个看起来像的数组$array = [    //...    'name' => ['value' => 'Raj KB'],    'street' => ['value' => 'Street ABC'],    'city' => ['value' => 'Dubai'],    'country_id' => ['value' => 'UAE'],    'region' => ['value' => 'DXB'],    'region_id' => ['value' => 11],    'zip_code' => ['value' => 12345],    'city_id' => ['value' => 22],    //...];我想对数组进行排序,以便键country_id, region, region_id, city,city_id连续出现,同时保留其他键的位置。预期产出 $array = [    //...    'name' => ['value' => 'Raj KB'],    'street' => ['value' => 'Street ABC'],    'country_id' => ['value' => 'UAE'],    'region' => ['value' => 'DXB'],    'region_id' => ['value' => 11],    'city' => ['value' => 'Dubai'],    'city_id' => ['value' => 22],    'zip_code' => ['value' => 12345],    //...];我试过:试验 #1uksort($array, function ($a, $b) {  $order = ['country_id' => 0, 'region' => 1, 'region_id' => 2, 'city' => 3, 'city_id' => 4];  if (isset($order[$a]) && isset($order[$b])) {    return $order[$a] - $order[$b];  } else {    return 0;  }});var_dump($array);试验 #2uksort($array, function ($a, $b) {  $order = ['country_id' => 0, 'region' => 1, 'region_id' => 2, 'city' => 3, 'city_id' => 4];  if (!isset($order[$a]) && !isset($order[$b])) {    return 0;  } elseif (!isset($order[$a])) {    return 1;  } elseif (!isset($order[$b])) {    return -1;  } else {    return $order[$a] - $order[$b];  }});var_dump($array);但其余订单不再维护。所以我希望这些自定义字段以相同的顺序出现,而不会破坏其他字段的位置。例如,name应该先出现等等。
查看完整描述

3 回答

?
海绵宝宝撒

TA贡献1809条经验 获得超8个赞

看起来你想要的东西很难用 PHP 的排序方法之一实现。此外,由于不匹配键的相对顺序不应该改变,我们可以争取比 O (nlogn)排序方法更好的时间复杂度。


因此,我建议编写一个函数,对两个数组 ( $array, $order) 进行一些迭代,以便按预期顺序收集键/值对。这构成了O(n+m)时间复杂度,其中n和m是两个数组的两个大小。


这是功能:


function sortadjacent($array, $order) {

    $insertAt = 0;

    foreach($array as $key => $_) {

        if (isset($order[$key])) break;

        $insertAt++;

    }


    $special = [];

    foreach($order as $key => $_) {

        if (isset($array[$key])) $special[$key] = $array[$key];

    }


    $result = [];

    foreach($array as $key => $value) {

        if (!isset($order[$key])) $result[$key] = $value;

        else if (count($result) == $insertAt) $result = array_merge($result, $special);

    }


    return $result;

}

你会这样称呼它:


$result = sortadjacent($array, $order);

请注意,此函数不会对 进行更改$array,而是在新数组中返回预期结果。


查看完整回答
反对 回复 2023-04-21
?
烙印99

TA贡献1829条经验 获得超13个赞

您的实现非常接近,但是您必须在比较函数中考虑这种情况,其中只有一个您想要的键存在,而其他任何键都不存在。如果你return 0在那种情况下,它们将在你的数组中的其他键中被破坏(因为它们在这种情况下的位置被认为是相等的)。


由于您还希望保留现有键的序列,并在之后插入其他“提取的”键country_id,因此您可以保留对原始排序顺序的引用,并使用它来解析与country_id其他字段相关的排序顺序(和其他字段之间以保持当前排序顺序)


通过处理这两种特殊情况以明确地对您希望自己依次出现的键进行排序,您将获得满足您要求的结果:


$order = ['country_id' => 1, 'region' => 2, 'region_id' => 3, 'city' => 4, 'city_id' => 5];

$preset_order = array_flip(array_keys($array));


uksort($array, function ($a, $b) use ($order, $preset_order) {

  if (isset($order[$a]) && isset($order[$b])) {

    return $order[$a] - $order[$b];

  } else if (isset($order[$a])) {

    return $preset_order['country_id'] - $preset_order[$b];

  } else if (isset($order[$b])) {

    return $preset_order[$a] - $preset_order['country_id'];

  } else {

    return $preset_order[$a] - $preset_order[$b];

  }

});

输出:


array(8) {

  'name' =>

  array(1) {

    'value' =>

    string(6) "Raj KB"

  }

  'street' =>

  array(1) {

    'value' =>

    string(10) "Street ABC"

  }

  'country_id' =>

  array(1) {

    'value' =>

    string(3) "UAE"

  }

  'region' =>

  array(1) {

    'value' =>

    string(3) "DXB"

  }

  'region_id' =>

  array(1) {

    'value' =>

    int(11)

  }

  'city' =>

  array(1) {

    'value' =>

    string(5) "Dubai"

  }

  'city_id' =>

  array(1) {

    'value' =>

    int(22)

  }

  'zip_code' =>

  array(1) {

    'value' =>

    int(12345)

  }

}


查看完整回答
反对 回复 2023-04-21
?
阿波罗的战车

TA贡献1862条经验 获得超6个赞

PHP 使用Quicksort,因此您不能只为要排序的元素返回有意义的值。在我看来,在这里使用 uksort 是个坏主意,因为您必须使用数组的当前索引作为值,但这是不可能的,因为您无法从比较函数内部访问旧数组的副本。您还需要知道第一个特殊值在哪个索引上。


所以我建议这样做,因为我认为用 uksort 做你想做的事是不可能的:


function customSort($array)

{

    $order = ['country_id' => 0, 'region' => 1, 'region_id' => 2, 'city' => 3, 'city_id' => 4];

    $keyArray = array();

    $sortedArray = array();

    foreach ($array as $i => $value) {

        $keyArray[] = $i;

    }

    $counter = 0;

    $hasStarted = false;

    $insertLater = array();

    for ($i = 0; $i < count($keyArray); $i++) {

        if ($hasStarted) {

            if ($counter < count($order)) {

                $sortedArray[array_search($counter, $order)] = $array[array_search($counter, $order)];


                $counter++;

                if (!isset($order[$keyArray[$i]])) {

                    array_push($insertLater, ["key" => $keyArray[$i], "value" => $array[$keyArray[$i]]]);

                }

                continue;

            }

        }


        if (count($insertLater) > 0) {

            $itemToInsert = array_shift($insertLater);

            $sortedArray[$itemToInsert["key"]] = $itemToInsert["value"];


            if (!isset($order[$keyArray[$i]])) {

                array_push($insertLater, ["key" => $keyArray[$i], "value" => $array[$keyArray[$i]]]);

            }

            continue;

        }

        if (isset($order[$keyArray[$i]]) && !$hasStarted) {

            $sortedArray[array_search($counter, $order)] = $array[array_search($counter, $order)];

            $hasStarted = true;

            $counter++;

            continue;

        }

        $sortedArray[$keyArray[$i]] = $array[$keyArray[$i]];

    }

    return $sortedArray;

}

It's may

不是最好的解决方案,但它有效 O(n)。


查看完整回答
反对 回复 2023-04-21
  • 3 回答
  • 0 关注
  • 145 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信