问题描述
以“漂亮的表格视图”方式打印出来的值后,是否有一种简单或简单的方法来排列值?
我用于测试数据集的代码是:
Item.first.update({:popularity => "1"})
我使用的是测试数据集,因为实际的数据集太多,并且更容易在较小的批次中工作,但是输出看起来像:
Map<Integer,List<Boolean>> map = new HashMap<>();
Random r = new Random(System.currentTimeMillis());
map.put(1000 + r.nextInt(50000),Arrays.asList(true,false,false));
map.put(100 + r.nextInt(50000),Arrays.asList(false,false));
map.put(1000 + r.nextInt(50000),true,true));
map.put(1000 + r.nextInt(50000),false));
map.put(10000 + r.nextInt(50000),true));
map.put(1000 + r.nextInt(5000),false));
map.put(1000 + r.nextInt(5000),true));
map.put(100 + r.nextInt(50000),true));
map.put(100 + r.nextInt(5000),true));
map.put(10000 + r.nextInt(50000),true));
int counter = 0;
StringBuilder sb = new StringBuilder();
for (Map.Entry<Integer,List<Boolean>> entry : map.entrySet()) {
sb.append(entry.getKey() + " = " + entry.getValue() + " \t");
if (counter == 5) {
System.out.println(sb.toString());
counter = 0;
sb.setLength(0);
}
counter++;
}
关键的Integer范围很大,这使我想到它的排列不正确,并且单词“ false”比单词“ true”长,因此,如果某些条目的值是false而不是true ,那么它也会弄乱衬里。但是,有没有万无一失或简单的方法来解决此问题?我尝试将30336 = [true,true] 2820 = [true,false] 17029 = [false,false] 10183 = [true,false] 14600 = [true,false] 9741 = [true,true]
32717 = [true,false] 3664 = [true,false] 22610 = [true,true] 49618 = [true,false] 43476 = [true,true]
4439 = [true,true] 1816 = [false,false] 57562 = [true,true] 23450 = [false,true] 21018 = [true,false]
35291 = [false,false] 40923 = [true,false] 5342 = [true,false] 32353 = [false,false] 48098 = [true,false]
41442 = [false,false] 39269 = [true,false] 11623 = [false,true] 45031 = [true,false] 23720 = [true,false]
23209 = [false,false] 37419 = [false,true] 26798 = [true,true] 1454 = [false,true] 2544 = [true,false]
12080 = [true,false] 26418 = [false,false] 24052 = [true,true] 50293 = [false,false] 25720 = [true,true]
与sb.append(entry.getKey() + " = " + entry.getValue() + " \t");
一起使用,但结果相似,几乎成一直线,但锯齿状。
解决方法
请注意,将StringBuilder
与字符串串联之类的混合使用是不一致的
sb.append(entry.getKey() + " = " + entry.getValue() + " \t");
相反,StringBuilder
应该按预期使用,例如
sb.append(entry.getKey()).append(" = ").append(entry.getValue()).append(" \t");
从那里开始。将调用链分成单个语句并根据需要插入填充只是一小步:
final String columnSeparator = "|";
final int keyWidth = 6,valueWidth = "false,".length() * 3;
int counter = 0;
StringBuilder sb = new StringBuilder();
for (Map.Entry<Integer,List<Boolean>> entry : map.entrySet()) {
int colStart = sb.length(),colEnd = colStart + keyWidth;
sb.append((int)entry.getKey());
while(sb.length() < colEnd) sb.insert(colStart,' '); // right aligned
sb.append(" = ");
colEnd = sb.length() + valueWidth;
sb.append(entry.getValue());
while(sb.length() < colEnd) sb.append(' '); // left aligned
if(counter == 5) {
sb.append(System.lineSeparator());
counter = 0;
}
else {
sb.append(columnSeparator);
counter++;
}
}
if(counter != 0) {
sb.setLength(sb.length() - columnSeparator.length());
sb.append(System.lineSeparator());
}
System.out.append(sb);
产生类似
的输出 14339 = [false,false,false]| 4869 = [true,false] | 17669 = [true,false] | 3526 = [false,false]| 1864 = [false,true,true] | 9224 = [false,true]
30152 = [true,false] | 19273 = [false,false]| 49547 = [true,false] | 5391 = [true,false] | 4943 = [false,false]| 39311 = [true,true]
7889 = [true,true] | 31893 = [false,false]| 49174 = [true,false] | 3094 = [true,true] | 16854 = [true,true] | 27094 = [true,false]
46234 = [true,false] | 22751 = [true,false] | 58339 = [false,false]| 49700 = [true,true] | 44901 = [true,false] | 49254 = [true,true]
24230 = [true,false] | 45799 = [true,false] | 39591 = [true,true] | 17450 = [false,false]| 30893 = [true,true] | 14189 = [true,true]
33583 = [false,true] | 29365 = [false,false]| 5563 = [false,true] | 17341 = [true,false] | 5245 = [true,false] | 18302 = [true,false]
4223 = [true,false]
您可以根据需要将columnSeparator
更改为" | "
以增加间距,或更改为" "
之类以仅使用空格或更改为空字符串。您还可以使keyWidth
和valueWidth
适应现实情况。
以上解决方案在循环中使用sb.insert
,这仅在以下情况下可行:我们知道必须移位的字符数很小(这是整数),并且迭代次数也很小(这里是,我们有四位或五位数字,因此最多重复两次。
当实际密钥长度之间的差异可能更大时,可以通过在String pad = " ".repeat(keyWidth);
循环之前插入for
并替换
while(sb.length() < colEnd) sb.insert(colStart,' ');
使用
if(sb.length() < colEnd) sb.insert(colStart,pad,colEnd - sb.length());
当您还将密钥类型更改为可能产生较大字符串的某种类型时,可以通过替换来完全避免insert
sb.append((int)entry.getKey());
while(sb.length() < colEnd) sb.insert(colStart,' '); // right aligned
使用
String key = String.valueOf(entry.getKey());
if(key.length() < keyWidth) sb.append(pad,keyWidth - key.length());
sb.append(key);
但是,这仅对于必须通过其toString()
方法附加的对象是有回报的,而不是通过Integer
附加而无需复制中间字符串的append(int)
键的结果。
如果列宽固定并且想要简单的解决方案,则可以使用
int counter = 0;
for(Map.Entry<Integer,List<Boolean>> entry : map.entrySet()) {
System.out.printf(++counter%5 != 0? "%6d = %-21s|": "%6d = %21s%n",entry.getKey(),entry.getValue());
}
if(counter % 5 != 0) System.out.println();
,
尝试
sb.append(entry.getKey() + "\t=\t" + entry.getValue() + " \t");