首页 > 解决方案 > 将 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 中实现这一点?

请让我知道是否需要任何澄清,或者如果您觉得缺少某些东西。

谢谢各位。

标签: javascriptjsontypescript

解决方案


首先,您需要一个二维矩形数组,而您的输入似乎是一个一维数组。将一维数组“分块”为二维数组很容易:

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和列中的每个元素,您还可以计算和,从数组右下角测量的元素的索引。数量是这些中的最小值。如果不是固定环,那么您可以检查四个运动案例中的每一个。如果那时你就在上面。如果那么你在左边。如果那你在底部。如果你在右边。我想我不会再强调这一点了。cnrncringringr === ringc === ringnr === 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环没有移动,而其他环移动了一个元素。您应该随意使用不同的数组,看看它是如何工作的。

Playground 代码链接


推荐阅读