PHP自定义排序:根据指定的键对数组进行手动排序

PHP自定义排序:根据指定的键对数组进行手动排序,php,arrays,sorting,Php,Arrays,Sorting,我有一个数组,看起来像 $array = [ //... 'name' => ['value' => 'Raj KB'], 'street' => ['value' => 'Street ABC'], 'city' => ['value' => 'Dubai'], 'country_id' => ['value' => 'UAE'], 'region' => ['value' => 'DX

我有一个数组,看起来像

$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\u id
region
region\u id
city
city\u 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],
    //...
];
我试过:

试用#1

uksort($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);
试验#2

uksort($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);
但其余的订单不再维持。
因此,我希望这些自定义字段以相同的顺序出现,而不破坏其他字段的位置。例如,<代码>名称<代码>应该首先出现,等等

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

由于您还希望保留现有键的序列,并在
国家/地区id
之后插入其他“提取”键,因此您可以保留对原始排序顺序的引用,并使用该引用解析与其他字段的
国家/地区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)
  }
}

看起来使用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
,而是在新数组中返回预期结果。

PHP使用的是快速排序,因此您不能只返回要排序的元素的敏感值。 在我看来,在这里使用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
函数customSort($array)
{
$order=['country\u id'=>0,'region'=>1,'region\u id'=>2,'city'=>3,'city\u id'=>4];
$keyArray=array();
$sortedArray=array();
foreach($i=>$value的数组){
$keyArray[]=$i;
}
$counter=0;
$hasStarted=false;
$insertLater=array();
对于($i=0;$i$keyArray[$i],“value”=>$array[$keyArray[$i]]);
}
继续;
}
}
如果(计数($insertLater)>0){
$itemToInsert=array\u shift($insertLater);
$sortedArray[$itemToInsert[“键”]=$itemToInsert[“值”];
如果(!isset($order[$keyArray[$i]])){
array_push($insertLater,[“key”=>$keyArray[$i],“value”=>$array[$keyArray[$i]]);
}
继续;
}
如果(isset($order[$keyArray[$i]])&&&!$hasStarted){
$sortedArray[array_search($counter,$order)]=$array[array_search($counter,$order)];
$hasStarted=true;
$counter++;
继续;
}
$sortedArray[$keyArray[$i]]=$array[$keyArray[$i]];
}
返回$Darray;
}
现在是五月

不是最好的解决方案,但它在O(n)中起作用。

一个想法,构建一个生成器,以您想要的顺序生成密钥(根据我对问题的理解,排序密钥组在第一个排序密钥出现时开始):

请注意,某些已排序关键帧的值将设置两次,但这并不重要,因为只有第一个设置决定关键帧的顺序


您甚至不需要发电机,您可以直接在一个回路中完成:

$ordered_keys = ['country_id', 'region', 'region_id', 'city', 'city_id'];

$flag = true;
$result = [];

foreach ($array as $k=>$v) {
    if ( $flag && in_array($k, $ordered_keys) ) {
        $flag = false;
        foreach ($ordered_keys as $key) {
            $result[$key] = $array[$key];
        }
    } else {
        $result[$k] = $array[$k];
    }
}

print_r($result);


其他想法:使用相同的键构建一个数组,但使用整数值

在第一个“有序键”之前,值在0-255范围内,在值在512 inf范围内之后。顺序键的值已在256-511范围内设置。(这些范围完全是任意的,您可以选择所需的范围。)

$ordered_key=['country_id'=>256,'region'=>257,'region_id'=>258,'city'=>259,'city_id'=>260];
$index=0;
$result=[];
foreach($k=>v的数组){
if(isset($ordered_key[$k])){
$result[$k]=$ordered_key[$k];
如果($index<255)$index=512;
}否则{
$result[$k]=$index++;
}
}
asort($结果);
foreach($k=>v的结果){
$result[$k]=$array[$k];
}
打印(结果);

uksort()
会完成这项工作我猜你为什么希望
name
提前出现,而
zip\u code
晚出现?您是否希望原始订单以某种方式发挥作用?我不明白其中的逻辑。我只想把它们按顺序分类,其他的应该按原样分类。但这并不是很好的定义。哪个元素(也出现在<代码中)
$ordered_keys = ['country_id', 'region', 'region_id', 'city', 'city_id'];

$flag = true;
$result = [];

foreach ($array as $k=>$v) {
    if ( $flag && in_array($k, $ordered_keys) ) {
        $flag = false;
        foreach ($ordered_keys as $key) {
            $result[$key] = $array[$key];
        }
    } else {
        $result[$k] = $array[$k];
    }
}

print_r($result);
$ordered_keys = ['country_id' => 256, 'region' => 257, 'region_id' => 258, 'city' => 259, 'city_id' => 260];

$index = 0;

$result = [];

foreach ($array as $k => $v) {
    if ( isset($ordered_keys[$k]) ) {
        $result[$k] = $ordered_keys[$k];
        if ( $index < 255 ) $index = 512;
    } else {
        $result[$k] = $index++;
    }
}

asort($result);

foreach($result as $k => $v) {
    $result[$k] = $array[$k];
}

print_r($result);