按字母顺序和数字对字符串中括号内的值进行排序的最佳方法?
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']"
我希望我需要为此实现自定义比较器,但我无法想出最有效的方法来解析这些字符串并进行排序。有人对此有建议的方法吗?
我会推荐以下步骤:
- 在“[”上拆分路径
- 遍历每个元素(我们将同时遍历两条路径,长度不保证相同)
- 如果元素以 ' 开头,您就知道它是一个单词,否则就是一个数字
- 如果元素属于同一类型,return 适当排序。 (您需要在排序前解析整数,不要忘记先删除']')
- 如果元素类型不同,return表示数字在前
- 重复直到其中一条路径到达终点
- 循环后,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();
…
我有一个 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']"
我希望我需要为此实现自定义比较器,但我无法想出最有效的方法来解析这些字符串并进行排序。有人对此有建议的方法吗?
我会推荐以下步骤:
- 在“[”上拆分路径
- 遍历每个元素(我们将同时遍历两条路径,长度不保证相同)
- 如果元素以 ' 开头,您就知道它是一个单词,否则就是一个数字
- 如果元素属于同一类型,return 适当排序。 (您需要在排序前解析整数,不要忘记先删除']')
- 如果元素类型不同,return表示数字在前
- 重复直到其中一条路径到达终点
- 循环后,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();
…