将嵌套 Javascript 对象数组转换为具有唯一键子键的 2D 数组

问题描述 投票:0回答:1

我想将嵌套对象数组转换为二维数组,我想将其用作 Google 表格中应用程序脚本批量更新的一部分。

我在应用程序脚本中使用对象数组作为输入,如下所示:

[
  {
    a: "val1",
    b: {
      x: "val2",
      y: "val3",
      z: "val4"
    },
    c: {
      s: "val5",
      t: "val6",
      r: "val7"
    },
  },
  {
    a: "val8",
    b: {
      x: "val9",
      z: "val10"
    },
    c: {
      t: "val11",
      r: "val12"
    },
  },
  {
    a: "val13",
    b: {
      y: "val14"
    },
    c: {
      s: "val15",
      t: "val16",
      z: "val17"
    }
  }
]

所以并非所有嵌套对象都有所有键。

由于对象的数量相当大,我正在寻找一种有效的方法来处理和创建一个二维数组,其中第一列保存未嵌套的唯一键,子键分组在一起,而每个其他列保存保存每个对象的值(其中有一个键值对)。如下表:

密钥_子密钥对象 1 值对象 2 值对象 3 值aval1val8val13b_xval2val9b_yval3val14b_zval4val10c_sval5val15c_tval6val11val16c_rval7val12c_zval17
我正在寻求避免嵌套的 for 循环来填充数组,因为它效率低下。

关于如何快速简洁地做到这一点有什么想法吗?

蒂亚

json google-apps-script google-sheets multidimensional-array
1个回答
0
投票
如果我理解正确的话,你有一个对象数组。每个对象都是一列,它的属性预计有一个扩展的 id(展平时)作为父 id 直到根元素的组合。

因此,生成的对象是完整扩展的 id 及其值列表的映射,作为数组,槽位置映射到相应的列。

展示比解释更容易...

所以这里有一个

fillBag

 函数,它将迭代传递的对象属性(递归地),同时填充传递的 
bag
 对象来保存最终结果:

//this function is fully commented in the live snippet function fillBag(prefix, colIndex, obj, bag){ Object.entries(obj).forEach( ([key, value])=>{ const index = `${prefix}${key}`; if(typeof(value) === 'string' || value instanceof String){ const values = (index in bag) ? bag[index] : bag[index] = []; values.length = colIndex; values.push(value); }else{ fillBag(index, colIndex, value, bag); } }); }
我使用了与您相同的输入数组,但我必须进行一些更正,因为您有一些语法错误缺少关闭

}

这是最终返回的对象:

{ "a": [ "val1", "val8", "val13" ], "bx": [ "val2", "val9", undefined], "by": [ "val3", undefined, "val14" ], "bz": [ "val4", "val10", undefined ] "cs": [ "val5", undefined, "val15" ], "ct": [ "val6", "val11", "val16" ], "cr": [ "val7", "val12", undefined ], "cz": [ undefined, undefined, "val17" ] }
如果您需要数组的数组,而不是将唯一键映射到数组值的对象,您还可以在此基础上使用 

Object.entries

Object.entries($bag) //...[ key, [values] ]

警告现在考虑到后面的列可能会堆积的事实,如果给定的索引不再显示,则该索引的值数组将不会包含最后一列后面的索引的未定义值属性名称最后一次出现的地方。为了纠正这种行为,在处理整个对象后最后一次访问生成的包,并且所有数组都根据最大数组的大小(总列数)调整大小。

function fillTrailingEmptyColumns(obj, cols){ Object.values(obj).forEach( values =>{ values.length = cols; }); }
稀疏数组和设置 .length 属性

值得一提的是,由于每个键的值都存储在一个数组中,因此该数组将包含给定索引没有任何值的列的未定义值。

进一步阅读稀疏数组和空槽可能会很有趣。

大多数人会感到非常惊讶,你实际上可以设置

Array.length

 属性

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Indexed_collections#sparse_arrays

const data = [ { a:"val1", b: { x:"val2", y:"val3", z:"val4" }, c: { s:"val5", t:"val6", r:"val7" } }, { a:"val8", b:{ x:"val9", z:"val10" }, c:{ t:"val11", r:"val12" } }, { a:"val13", b:{ y:"val14" }, c:{ s:"val15", t:"val16", z:"val17" } } ] const bag = {}; //fills the bag with key->values data.forEach( (obj, i) => fillBag('', i, obj, bag)); //this is an information I could grab from data.length as well const cols = findMaxColumns(bag); //fills the trailing missing columns in bag for each key fillTrailingEmptyColumns(bag, cols); console.log(bag); //returns the maximum number of columns descrived in the obj function findMaxColumns(obj){ return Object.values(obj).reduce( (max, value)=>{ if(value.length > max) return value.length; else return max; }, 0 ); } function fillTrailingEmptyColumns(obj, cols){ Object.values(obj).forEach( values =>{ values.length = cols; }); } //fills the bag object with values coming from obj at the given prefix and colIndex function fillBag(prefix, colIndex, obj, bag){ //for each entry in obj (an entry is key-value property pair) Object.entries(obj).forEach( ([key, value])=>{ //current index based on prefix const index = `${prefix}${key}`; //if the current property value is a string if(typeof(value) === 'string' || value instanceof String){ //if composed key exists in bag if(index in bag){ let values = bag[index]; //change the size of values in the array based on the current column values.length = colIndex; //push the current value in the array of values for this key values.push(value); } //else if composed key doesn't exist yet in bag else{ //create a new array filled with nulls until now let values = []; //change the size of values in the array based on the current column values.length = colIndex; //push the current value in the array of values for this key values.push(value); //sets the key in the bag for its first time bag[index] = values; } } //else if the obj is not a string (thus a nested properties bag) else{ //recursively call fillBag with the current composed index and passing the current value fillBag(index, colIndex, value, bag); } }); }

© www.soinside.com 2019 - 2024. All rights reserved.