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

yh2wf1be  于 2023-01-27  发布在  Java
关注(0)|答案(1)|浏览(89)

我想将一个嵌套对象数组转换为一个2D数组,并将其用作Google Sheets中应用程序脚本batchUpdate的一部分。
我使用一个对象数组作为输入,在应用程序脚本如下:

[{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值|
| - ------|- ------|- ------|- ------|
| 项目a|值1|val8|val13|
| B_x|val 2值|val9||
| B_y|val3||第14节|
| B_z|val4|阀门10||
| c_s|val5||val15|
| c_t|val6|第11节|val16|
| c_r|val7|第12节||
| c_z|||val17|
我正在寻求避免嵌套的for循环来填充数组,因为这将是低效的。
有什么想法如何做到这一点快速和简洁?
短暂性脑缺血发作

osh3o9ms

osh3o9ms1#

如果我理解正确的话,您有一个对象数组..每个对象都是一列,并且其属性应该有一个扩展的id(当展平时),作为直到根元素的父id的组成。
以便得到的对象是完全展开的id和它们的值列表的Map,作为一个数组,插槽位置Map到相应的列。
展示比解释容易...
这里有一个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" ]
}

如果你需要一个数组的数组,而不是一个将唯一键Map到数组值的对象,你也可以在此基础上使用Object.entries

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

***警告***它现在考虑到后面的列可能会堆积,如果给定的索引不再显示,则该索引的值数组w将不包含属性名称最后一次出现的最后一列后面的索引的未定义值。要更正该行为,在处理整个对象后最后一次访问生成的包,所有数组都用最大数组的大小(总列数)调整大小。

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);
      }
    });
    
}

相关问题