【问题标题】:Change keys of array object after inserting in the middle/first of array object插入数组对象的中间/第一个后更改数组对象的键
【发布时间】:2021-01-28 22:21:15
【问题描述】:

我正在开发一个类似 Excel 的应用程序,但我遇到了多维数组的问题。

我有如下所示的列数组:

var columns = [
  {A: {binding: "A",header: "A"},
  {B: {binding: "B",header: "B"},
  {C: {binding: "C",header: "C"}
];

然后我有一个数组表示每个单元格的值,如下所示:

var data = [
   {A:"row 0 col A", B: "row 0 col B", C:"row 0 col C"},
   {A:"row 1 col A", B: "row 1 col B", C:"row 1 col C"},
   {A:"row 2 col A", B: "row 2 col B", C:"row 2 col C"}
];

columns 数组中对象的binding 属性将用于获取每一行的列值。

但是,在某些字母键的中间插入新列时,我遇到了问题。 假设我需要在列A 和列B 之间建立一个新列。结果如下所示:

var columns = [
  {A: {binding: "A",header: "A"},
  {B: {binding: "B",header: "B"},
  {C: {binding: "C",header: "C"},
  {D: {binding: "D",header: "D"}
];

它只是将新列推送到columns

我想我必须在每一行的所有字母键之间插入一个新单元格/项目,并重命名所有键(在这种情况下,将键 B 重命名为 CC 重命名为 D,等)。

我希望结果如下所示:

var data = [
   {A:"row 0 col A", B:"new col inserted here", C:"row 0 col B", D:"row 0 col C"},
   {A:"row 1 col A", B:"new col inserted here", C:"row 1 col B", D:"row 1 col C"},
   {A:"row 2 col A", B:"new col inserted here", C:"row 2 col B", D:"row 2 col C"}
];

如果我要重命名所有这些键,我会担心性能问题,尤其是当我有 120 列 (A-DZ) 和 100 行时。

我有两个问题:

  • 最有效的方法是什么?
  • 如果我有很多行和列,重命名这些键时会出现性能问题吗?

【问题讨论】:

  • 插入时,如何选择新的最右边的列名?这里是“D”,但在任意情况下如何确定呢?
  • 我不会担心重命名 ~12K 键的性能,除非我测试并发现这是一个问题。我希望它很快。
  • @ScottSauyet 我使用此代码stackoverflow.com/questions/12504042/…,但我需要对其稍作修改,以便设置起始字符。示例:我希望函数从 char 'C' 开始增量
  • 我知道您希望新列在 UI 中的 A 和 B 之间可见,但为什么需要在 A 和 B 之间显示它?你是用顺序索引来访问的吗?比如索引 0 表示 A,索引 1 表示 B 等等
  • 我认为@Andam 就在这里。对象本质上是无序的容器。如果您维护一个表示列的属性名称的外部数组,那么插入只是更新该数组(并可能在每一行上添加新属性名称的默认值。)

标签: javascript arrays arrayobject


【解决方案1】:

不要对行或列使用文字名称。

为了提高性能,将一个数据对象直接链接到列和/或行(在元胞数组中创建一些属性,例如 _row 和 _col),并将 _cells 放在行和列中。

例子:

var data = {};

data.rows = [
{idx: 'A', _cells: []},
{idx: 'B', _cells: []},
{idx: 'C', _cells: []}
]

data.cols = [
{idx: 'A', _cells: []},
{idx: 'B', _cells: []},
{idx: 'C', _cells: []}
]

data.cells = [];

function addCell( rowIdx, colIdx, value) {
  var row = data.rows[rowIdx];
  var col = data.cols[rowIdx];
  var cell = {value: value, _row: row, _col: col};
  data.cells.push(cell);
  row._cells.push(cell);
  col._cells.push(cell);
}

for(var r=0; r<3; r++)
  for (var c=0;c<3; c++)
    addCell(r,c,'row ' + r + ' col ' + String.fromCharCode(65 + c));

console.log(data);

所以...您可以毫无困难地插入/删除/排序/重命名行。

您可以毫无困难地插入/删除/排序/重命名列中的所有单元格。

如果顺序很重要,您可以“映射”按行/列排序的单元格

【讨论】:

  • 一种有趣的方法,但我会担心各种循环引用。单元格需要引用回其行和列是否有充分的理由?
  • 好吧,如果您需要创建一个新的“完整”行或列,则不需要!但是如果你想在“之前”创建一个单元格,那么在“cols”中的行移位会容易得多
【解决方案2】:

首先,我认为 Andam 对问题的评论是正确的。当然有更好的方法可以从一组对象中制作出类似电子表格的显示。我的想法只是维护一个包含(至少)列标题和要在列中显示的属性名称的标题对象数组。然后插入一列将只涉及更新该数组(并且可能为您的行默认该属性值。)

但是您仍然希望按照您的问题建议的方式进行操作,这是一个可能的实现方式:

// Utility function
const alphaNum = (cs) => (n) => 
  n < cs.length
    ? cs [n % cs.length]
    : alphaNum (cs) (Math.floor(n / cs.length) - 1) + cs [n % cs.length]

// Helper function
const toColumnName = alphaNum ('ABCDEFGHIJKLMNOPQRSTUVWXYZ' .split (''))

// Main function
const insertColumn = (index, data, makeVal) => 
  data .map ((item, i, arr, vals = Object .values (item)) => Object.fromEntries ([
    ...vals .slice (0, index),
    makeVal (item, i, index),
    ...vals .slice (index)
  ] .map ((item, i) => [toColumnName(i), item])))

// Dummy cell creation function
const createCell = (row, rowNbr, colNbr) => 
  `new col inserted at row ${rowNbr} col ${colNbr}`

// Sample data
const data = [{A: 'row 0 col A', B: 'row 0 col B', C: 'row 0 col C'}, {A: 'row 1 col A', B: 'row 1 col B', C: 'row 1 col C'}, {A: 'row 2 col A', B: 'row 2 col B', C: 'row 2 col C'}]

// Demo
console .log (insertColumn (1, data, createCell))
.as-console-wrapper {max-height: 100% !important; top: 0}
  • toColumnName 简单地将(从零开始的)索引转换为类似电子表格的列标识符('A''B''C'、...'Z'、@ 987654328@, 'AB', ... 'AZ', 'BA', 'BB', ... 'BZ', ... 'ZZ', 'AAA', ...)

  • 它使用 alphaNum,它基于一组字符来表示所使用的伪数字,从而完成繁重的工作。

  • insertColumn 是主要功能。它采用插入列的(从零开始的)索引、数据和用于创建单元格值的函数。它通过提取值来更改每一行,插入由您的函数创建的新值,然后从新索引映射回列名,然后使用 Object .fromEntries 重建对象。

这里我们传递了一个虚拟的列创建函数,我们可以注意到,它传递了行对象、行索引和新列索引。

我不知道它将如何处理您的真实数据,但您描述的数字不会让我过分担心。你可以在这里做一些小的优化。例如,我们不需要重新计算每一行的列名,而是可以缓存它们。但是,如果性能不符合您的期望,我只会打扰他们。

再说一次,我认为有更好的方法来解决可能的潜在问题。

【讨论】:

  • 我同意你的看法,有很多更好的方法可以解决这个问题,就像@eduardo 建议的那样。但我不能像那样更改我的“准备发布”应用程序的数据结构。
  • 明白。这就是为什么我给出了一个我认为可能会有所帮助的答案。但如果你有时间重构它,它可能是一个更好的方法。
猜你喜欢
  • 2020-02-07
  • 1970-01-01
  • 1970-01-01
  • 2017-07-17
  • 2011-10-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多