【问题标题】:Best way to sort bracketed values in a string both alphabetically and by number?按字母顺序和数字对字符串中括号内的值进行排序的最佳方法?
【发布时间】:2021-12-24 03:56:50
【问题描述】:

我有一个 ArrayList,其中包含字符串格式的 JSONpath 列表,类似于以下内容:

"$['book'][0]['title']"
"$['book'][1]['title']"
"$['book'][2]['title']"
...
"$['book'][10]['title']"
"$['movie']['series'][0]['title']"
"$['movie']['series'][1]['title']"
"$['movie']['series'][2]['title']"
...
"$['movie']['series'][10]['title']"

这里的实际数据不用担心,它是垃圾,最终的结构是带有一系列括号的$,其中括号之间的值有时是字母字符串,有时是数字。

我需要按顺序对这些 ArrayList 项进行排序,如果括号之间的节点在引号中,则按字母顺序排序,但如果在引号之外,则应按数字排序。 IE。顺序应该是:

"$['book'][0]['title']"
"$['book'][1]['title']"
"$['book'][2]['title']"
"$['book'][10]['title']"
"$['movie']['series'][0]['title']"
"$['movie']['series'][1]['title']"
"$['movie']['series'][2]['title']"
"$['movie']['series'][10]['title']"

不是:

"$['book'][0]['title']"
"$['book'][1]['title']"
"$['book'][10]['title']"
"$['book'][2]['title']"
"$['movie']['series'][0]['title']"
"$['movie']['series'][1]['title']"
"$['movie']['series'][10]['title']"
"$['movie']['series'][2]['title']"

我希望我需要为此实现一个自定义比较器,但我无法想出最有效的方法来解析这些字符串并进行排序。有人对此有建议的方法吗?

【问题讨论】:

    标签: java sorting comparator


    【解决方案1】:

    字符串的自定义比较器应将输入字符串转换为列表/数组,然后可以通过相同索引处的适当元素进行比较:

    String[] data = {
            "$['book'][0]['title']",
            "$['book'][0]['title']['part1']",
            "$['book'][0]['title']['2']",
            "$['book'][1]['title']",
            "$['book'][prequel]['title']",
            "$['book'][sequel]['title']",
            "$['movie']['series'][1]['title']",
            "$['book'][10]['title']",
            "$['movie']['series'][10]['title']",
            "$['book'][2]['title']",
            "$['movie']['series'][0]['title']",
            "$['movie']['series'][2]['title']"
    };
    
    Arrays.sort(data, MyClass::customCompare);
    

    方法customCompare 应调用转换器并在需要时应用“整数”条件比较:

    private static int customCompare(String s1, String s2) {
        List<String> l1 = convert(s1);
        List<String> l2 = convert(s2);
        int res = 0;
        for (int i = 0, n = Math.min(l1.size(), l2.size()); res == 0 && i < n; i++) {
            String e1 = l1.get(i);
            String e2 = l2.get(i);
            if (e1.matches("\\d+") && e2.matches("\\d+")) {
                res = Integer.compare(Integer.valueOf(e1), Integer.valueOf(e2));
            } else {
                res = e1.compareTo(e2);
            }
        }
        return res != 0 ? res : Integer.compare(l1.size(), l2.size());
    }
    

    方法convert 清除不必要的字符并从输入中创建一个字符串列表:

    private static List<String> convert(String str) {
        return Arrays.stream(str.split("[$'\\[\\]]"))
                     .filter(x -> !x.isEmpty())
                     .collect(Collectors.toList());
    }
    

    排序后的输出:

    $['book'][0]['title']
    $['book'][0]['title']['2']
    $['book'][0]['title']['part1']
    $['book'][1]['title']
    $['book'][2]['title']
    $['book'][10]['title']
    $['book'][prequel]['title']
    $['book'][sequel]['title']
    $['movie']['series'][0]['title']
    $['movie']['series'][1]['title']
    $['movie']['series'][2]['title']
    $['movie']['series'][10]['title']
    

    【讨论】:

      【解决方案2】:

      我会推荐以下步骤:

      1. 在'['上分割路径
      2. 遍历每个元素(我们将同时遍历两条路径,长度不保证相同)
      3. 如果元素以'开头,你知道它是一个单词,否则,一个数字
      4. 如果元素属于同一类型,则返回相应的排序。 (您需要在排序前解析整数,不要忘记先删除']')
      5. 如果元素属于不同类型,则返回数字在前的指示。
      6. 重复直到其中一条路径到达终点
      7. 循环之后,返回较短路径先出现的指示

      这个解决方案本身难以置信,但在空间和时间上都是 O(n)。它在时间上应该比排序本身更复杂,在空间上也不会更复杂。

      【讨论】:

        【解决方案3】:

        这有点难看,但这会起作用。去掉前面的“$[”和后面的“]”,然后在“][”上拆分(因为String::split使用正则表达式,我把左括号转义为"]\\[")。

        然后循环遍历这些值,如果它们以单引号开头,则将它们作为字符串进行比较,否则将它们作为整数进行比较。如果它们属于不同类型,我只是比较第一个字符。这会将字符串放在数字之前。如果你愿意,把它换掉。此外,如果一条路径比另一条路径长,则它只比较两个长度中较小的一个。当较短的路径(前缀)的所有元素都等于较长的路径时,您可能希望较长或较短的路径先出现,我不知道。

        Comparator<String> jsonpath = (path1, path2) -> {
            String[] j1 = path1.substring(2, path1.length() - 1).split("]\\[");
            String[] j2 = path2.substring(2, path2.length() - 1).split("]\\[");
            for (int i = 0; i < Math.min(j1.length, j2.length); ++i) {
                int c;
                char c1 = j1[i].charAt(0);
                char c2 = j2[i].charAt(0);
                if (c1 == '\'' && c2 == '\'') {
                    c = j1[i].compareTo(j2[i]);
                } else if (c1 != '\'' && c2 != '\'') {
                    c = Integer.valueOf(j1[i]).compareTo(Integer.valueOf(j2[i]));
                } else c = c1 - c2;
                if (c != 0) return c;
            }
            return 0;
        };
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-10-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-07-23
          • 2021-12-19
          • 1970-01-01
          相关资源
          最近更新 更多