javascript - 将 JSON 的内容旋转为表格
问题描述
如果 JSON 为 [1,2,3,4],则表格顺时针旋转一次。在第二个示例中,5 是固定的,所有其他数字都顺时针旋转了一次。系统应提供以下输出并以 JSON 格式打印。
input output
1 2 3 1
->
3 4 4 2
input output
1 2 3 4 1 2
->
4 5 6 7 5 3
7 8 9 8 9 6
我如何在 javascript/typescript 中实现这一点?
请让我知道是否需要任何澄清,或者如果您觉得缺少某些东西。
谢谢各位。
解决方案
首先,您需要一个二维矩形数组,而您的输入似乎是一个一维数组。将一维数组“分块”为二维数组很容易:
const chunk = <T,>(arr: T[], size: number): T[][] =>
Array.from({ length: Math.ceil(arr.length / size) },
(_, i) => arr.slice(i * size, (i + 1) * size));
让我们看看实际情况;从 25 个元素的数组开始:
const width = 5;
const origArray = Array.from({ length: width * width }, (_, i) => (i + 1));
console.log(origArray); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
// 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]
分块版本有 5 个元素,每个元素有 5 个元素:
const chunkedArr = chunk(origArray, width);
console.log(chunkedArr); // [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10],
// [11, 12, 13, 14, 15], [16, 17, 18, 19, 20], [21, 22, 23, 24, 25]]
不过,这不是可视化“旋转”的好方法,所以让我们以 2D 形式显示它:
displayArr(chunkedArr); /* "
1, 2, 3, 4, 5
6, 7, 8, 9, 10
11, 12, 13, 14, 15
16, 17, 18, 19, 20
21, 22, 23, 24, 25" */
其中displayArr()
定义为
const displayArr = <T,>(arr: T[][]) =>
console.log("\n" + arr.map(c => c.map(v => String(v).padStart(4))).join("\n"))
好的,一旦我们有了一个二维数组,我们如何将它“旋转”一个元素?大概您将每个元素移动一个位置(向上、向右、向下或向左),具体取决于它在数组中的位置。据我所知,这并不是真正的旋转,或者至少不是刚性旋转。
相反,您正在做的是将数组划分为“环”,并在其自己的环中顺时针移动每个元素。在上面的 25 元素数组中,有 3 个环。最外面的环(我称之为“环 0”)由十六个元素(1,2,3,4,5,10,15,20,25,24,23,22,21,16,11,6)组成,中间环(我称之为“环 1”)由八个元素(7、8、9、14、19、18、17、12)和最内环(我称之为“环 2”)组成由一个元素 (13) 组成。
其环顶行中不在其环最右列中的任何元素都应向右移动。然后,其环左列中的任何元素都应向上移动。然后,其环底行中的任何元素都应向左移动。剩下的任何东西都应该向下移动。如果我们可以通过编程方式确定每个元素的“环号”,以及该元素是在其环的顶部、右侧、底部还是左侧,我们就可以相应地调整元素索引。
一个问题是,当数组有奇数行和奇数列时,最里面的环不能真正移动。例如,如果您有一个 5×7 数组,则中间会有一个 1×3 环。像“A、B、C”这样的 1×3 环实际上不能去任何地方。(我的意思是,你可以想出一些其他的方式来改变它,比如“C、A、B”之类的,但这与其他环完全不同。)所以我们需要检测数组是否有这样的一个“固定环”并且不移动该环中的任何元素。
这是一种可能的实现:
function rotateClockwise<T>(arr: T[][]) {
const ret: any[][] = arr.map(c => c.map(v => "")); // blank copy
const rows = arr.length;
if (rows === 0) return ret;
const cols = arr[0].length;
if (!arr.every(l => l.length === cols)) throw new Error("Not rectangular");
const stationaryRing = (rows % 2 !== 0) && (cols % 2 !== 0) ?
(Math.min(rows, cols) - 1) / 2 : -1;
for (let r = 0; r < rows; r++) {
let nr = rows - 1 - r;
for (let c = 0; c < cols; c++) {
let nc = cols - 1 - c;
const ring = Math.min(r, nr, c, nc);
let [rNew, cNew] = [r, c];
if (ring !== stationaryRing) {
if (r === ring && nc !== ring) cNew++; // top row moves right (except for rightmost)
else if (c === ring) rNew--; // left column moves up
else if (nr === ring) cNew--; // bottom row moves left
else rNew++; // right column moves down
}
ret[rNew][cNew] = arr[r][c];
}
}
return ret;
}
那里的逻辑反映了上述讨论。对于从数组左上角测量的行r
和列中的每个元素,您还可以计算和,从数组右下角测量的元素的索引。数量是这些中的最小值。如果不是固定环,那么您可以检查四个运动案例中的每一个。如果那时你就在上面。如果那么你在左边。如果那你在底部。如果你在右边。我想我不会再强调这一点了。c
nr
nc
ring
ring
r === ring
c === ring
nr === ring
(nc === ring)
让我们看看上面的 25 元素数组是如何工作的:
displayArr(rotateClockwise(chunkedArr)); /*
6, 1, 2, 3, 4
11, 12, 7, 8, 5
16, 17, 13, 9, 10
21, 18, 19, 14, 15
22, 23, 24, 25, 20" */
看起来挺好的。最外圈旋转一圈,中间圈也是如此,而最内圈没有移动。
为了确保这适用于不同的情况,让我们尝试一个 5×7 数组:
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abc"+
"defghijklmnopqrstuvwxyz!@#$%^&*()_-+=[]{}\\|;:'\",<.>/?`~";
const makeArr = (rows: number, cols: number) =>
Array.from({ length: rows }, (_, r) =>
Array.from({ length: cols }, (_, c) => chars.charAt(r * cols + c)));
const arr = makeArr(5, 7);
displayArr(arr); /* "
A, B, C, D, E, F, G
H, I, J, K, L, M, N
O, P, Q, R, S, T, U
V, W, X, Y, Z, 0, 1
2, 3, 4, 5, 6, 7, 8" */
displayArr(rotateClockwise(arr)); /* "
H, A, B, C, D, E, F
O, P, I, J, K, L, G
V, W, Q, R, S, M, N
2, X, Y, Z, 0, T, U
3, 4, 5, 6, 7, 8, 1" */
看起来也不错。您可以看到最里面的Q, R, S
环没有移动,而其他环移动了一个元素。您应该随意使用不同的数组,看看它是如何工作的。
推荐阅读
- java - 如果我在依赖关系树中看不到它,如何从 Maven 中排除它?
- python - 如何为 Visual Studio 2019 安装 matplotlib-cpp.h?
- javascript - 使用 React 在功能组件内使用 flexbox 进行样式设置
- javascript - 如何过滤掉多个对象值,包括匹配字符串的嵌套对象值?
- javascript - 在 html 中加载大型压缩 json
- sql - 另一个表中的计数尚不存在,无法在 WHERE 中过滤
- sql-server - 如何为 Azure Blob 文件夹配置保留期?
- javascript - 错误类型错误:newAsBuilt.asBuiltWoList 未定义
- spring - 如何使用带有 groovy 的 gradle 初始化 kotlin spring 项目
- ios - 将 KeyPaths 存储在 Dictionary/HashMap 中