按任意数量的子字符串对字符串数组进行排序 [英] Sorting an array of strings by arbitrary numbers of substrings

查看:98
本文介绍了按任意数量的子字符串对字符串数组进行排序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试修改

我已经在 uasort 上待了一段时间(必须保持键关联),并且已经接近了.诚然,这是由于绝望的结果而不是任何严格的方法所造成的.不太了解 uasort 的工作方式在某种程度上限制了我.

 //获得列表中名称空间组件的最大数量$ ns_counts = array_map(function($ val){返回计数(explode(爆炸"("\"",$ val ["namespace"))));},$ indexes);$ limit = max($ ns_counts);for($ depth = 0; $ depth< = $ limit; $ depth ++){uasort($ indexes,function($ first,$ second)use($ depth,$ limit){$ fexp = explode("\\",$ first [命名空间"]);$ sexp = explode("\\",$ second ["namespace"]);如果($ depth === $ limit){//为什么有帮助?array_pop($ fexp);array_pop($ sexp);}$ fexp = array_slice($ fexp,0,$ depth + 1,true);$ sexp = array_slice($ sexp,0,$ depth + 1,true);$ fimp = implode(",$ fexp);$ simp = implode(",$ sexp);//回显"$ depth:$ fimp<->$ simp \ n";返回strnatcmp($ fimp,$ simp);});}回声json_encode($ indexes,JSON_PRETTY_PRINT); 

这给了我正确排序的输出,但是在底部而不是顶部有更深的名称空间:

 <代码> {"31":{名称空间":"M";},"12":{名称空间":"A \\ Bees"},"16":{名称空间":"A \\ Sea"},"3":{名称空间":"A \\ B \\ Bee"},"9":{名称空间":"A \\ B \\ See"},"38":{名称空间":"A \\ B \\ C \\ Dee"},"51":{" namespace":" X \\ Wye"},"26":{名称空间":"X \\ Y \\ Zed"}} 

我想我可能必须为每个级别的命名空间构建一个单独的数组并对其进行单独排序,但是在如何做到这一点上有一个空白.有什么建议可以完成这项工作的最后一步,或者有什么完全不同但又不涉及太多循环的事情?

解决方案

我认为以下方法应该有效:

  uasort($ indexes,静态函数(数组$ entry1,数组$ entry2):int {$ ns1Parts = explode('\\',$ entry1 ['namespace']);$ ns2Parts = explode('\\',$ entry2 ['namespace']);$ ns1Length = count($ ns1Parts);$ ns2Length = count($ ns2Parts);for($ i = 0; $ i< $ ns1Length& isset($ ns2Parts [$ i]); $ i ++){$ isLastPartForNs1 = $ i === $ ns1Length-1;$ isLastPartForNs2 = $ i === $ ns2Length-1;如果($ isLastPartForNs1!== $ isLastPartForNs2){返回$ isLastPartForNs1< =>$ isLastPartForNs2;}$ nsComparison = $ ns1Parts [$ i]< =>$ ns2Parts [$ i];如果($ nsComparison!== 0){返回$ nsComparison;}}返回0;}); 

它的作用是:

  • 将名称空间拆分为多个部分,
  • 比较从第一个部分开始的每个部分,然后:
    • 如果我们只讨论最后一部分而不是另一部分,则优先考虑具有最多部分的那一部分,
    • 否则,如果各自的部分不同,则按字母顺序优先于另一部分.

> 演示

I'm attempting to modify the OrderedImportsFixer class in php-cs-fixer so I can clean up my files the way I want. What I want is to order my imports in a fashion similar to what you'd see in a filesystem listing, with "directories" listed before "files".

So, given this array:

$indexes = [
    26 => ["namespace" => "X\\Y\\Zed"],
    9 =>  ["namespace" => "A\\B\\See"],
    3 =>  ["namespace" => "A\\B\\Bee"],
    38 => ["namespace" => "A\\B\\C\\Dee"],
    51 => ["namespace" => "X\\Wye"],
    16 => ["namespace" => "A\\Sea"],
    12 => ["namespace" => "A\\Bees"],
    31 => ["namespace" => "M"],
];

I'd like this output:

$sorted = [
    38 => ["namespace" => "A\\B\\C\\Dee"],
    3 =>  ["namespace" => "A\\B\\Bee"],
    9 =>  ["namespace" => "A\\B\\See"],
    12 => ["namespace" => "A\\Bees"],
    16 => ["namespace" => "A\\Sea"],
    26 => ["namespace" => "X\\Y\\Zed"],
    51 => ["namespace" => "X\\Wye"],
    31 => ["namespace" => "M"],
];

As in a typical filesystem listing:

I've been going at uasort for a while (key association must be maintained) and have come close. Admittedly, this is due more to desperate flailing than any sort of rigorous methodology. Not really having a sense of how uasort works is kind of limiting me here.

// get the maximum number of namespace components in the list
$ns_counts = array_map(function($val){
    return count(explode("\\", $val["namespace"]));
}, $indexes);
$limit = max($ns_counts);

for ($depth = 0; $depth <= $limit; $depth++) {
    uasort($indexes, function($first, $second) use ($depth, $limit) {
        $fexp = explode("\\", $first["namespace"]);
        $sexp = explode("\\", $second["namespace"]);
        if ($depth === $limit) {
            // why does this help?
            array_pop($fexp);
            array_pop($sexp);
        }
        $fexp = array_slice($fexp, 0, $depth + 1, true);
        $sexp = array_slice($sexp, 0, $depth + 1, true);
        $fimp = implode(" ", $fexp);
        $simp = implode(" ", $sexp);
        //echo "$depth: $fimp <-> $simp\n";
        return strnatcmp($fimp, $simp);
    });
}
echo json_encode($indexes, JSON_PRETTY_PRINT);

This gives me properly sorted output, but with deeper namespaces on the bottom instead of the top:

{
    "31": {
        "namespace": "M"
    },
    "12": {
        "namespace": "A\\Bees"
    },
    "16": {
        "namespace": "A\\Sea"
    },
    "3": {
        "namespace": "A\\B\\Bee"
    },
    "9": {
        "namespace": "A\\B\\See"
    },
    "38": {
        "namespace": "A\\B\\C\\Dee"
    },
    "51": {
        "namespace": "X\\Wye"
    },
    "26": {
        "namespace": "X\\Y\\Zed"
    }
}

I'm thinking I may have to build a separate array for each level of namespace and sort it separately, but have drawn a blank on how I might do that. Any suggestions for getting the last step of this working, or something completely different that doesn't involve so many loops?

解决方案

I believe the following should work:

uasort($indexes, static function (array $entry1, array $entry2): int {  
    $ns1Parts = explode('\\', $entry1['namespace']);
    $ns2Parts = explode('\\', $entry2['namespace']);

    $ns1Length = count($ns1Parts);
    $ns2Length = count($ns2Parts);

    for ($i = 0; $i < $ns1Length && isset($ns2Parts[$i]); $i++) {
        $isLastPartForNs1 = $i === $ns1Length - 1;
        $isLastPartForNs2 = $i === $ns2Length - 1;

        if ($isLastPartForNs1 !== $isLastPartForNs2) {
            return $isLastPartForNs1 <=> $isLastPartForNs2;
        }

        $nsComparison = $ns1Parts[$i] <=> $ns2Parts[$i];

        if ($nsComparison !== 0) {
            return $nsComparison;
        }
    }

    return 0;
});

What it does is:

  • split namespaces into parts,
  • compare each part starting from the first one, and:
    • if we're at the last part for one and not the other, prioritize the one with the most parts,
    • otherwise, if the respective parts are different, prioritize the one that is before the other one alphabetically.

Demo

这篇关于按任意数量的子字符串对字符串数组进行排序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆