按字母顺序和数字对字符串中括号内的值进行排序的最佳方法?

Best way to sort bracketed values in a string both alphabetically and by number?

我有一个 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']"

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

我会推荐以下步骤:

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

这个解决方案本身并不令人难以置信,但在space 和时间上都是 O(n)。它在时间上应该没有排序本身那么复杂,并且在 space.

中不会更复杂

它有点难看,但它会起作用。删除前导的“$[”和尾随的“]”,然后拆分为“][”(因为 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;
};

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

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']

要将每行字符串按其数据类型拆分并排序,Java代码会很长。

建议您使用 open-source Java 软件包 SPL 来完成此操作。两行代码就够了:

A
1 =file("data.txt").read@n()
2 =A1(A1.(mid(~,4,len(~)-5).split@p("][")).psort())

SPL 提供 JDBC 驱动程序供 Java 调用。只需将上述 SPL 脚本存储为 sort.splx 并在调用存储过程时在 Java 应用程序中调用它:

…
Class.forName("com.esproc.jdbc.InternalDriver");
con= DriverManager.getConnection("jdbc:esproc:local://");
st = con.prepareCall("call sort()");
st.execute();
…