获取嵌套对象和数组的值 - 使用plunker

时间:2017-06-08 23:21:29

标签: javascript arrays angular

我有两个数组,带有嵌套对象,作为API端点调用的一部分下载,一个(预览)只有数字。

示例:

[{
  obj1:[1, 2],
  obj2:[3, 4]
}]

我不得不再次调用另一个端点,以获取带字符串的ID列表

示例:

[{
  obj1:[{
    id:1,
    name:'string_name1'
  }, {
    id:2, 
    name:'string_name2'
  }]
}, {
  obj2:[{
    id:3, 
    name:'string_name3'
  }, {
    id:4, 
    name:'string_name4'
  }]
}];

我需要将ID与第一个对象数组匹配,因此我的网页上会显示字符串/文本值

我有两个功能

第一个,从预览数组中提取数字并将它们推送到我将用于在页面上显示的我自己的可编辑数组

这是函数运行前的数组

 objName = [['obj1'], ['obj2']];

这是第一个函数,将预览中的名称与数组中的名称相匹配并推送值

    setNumbers(){
     for(let i = 0; i < this.objName.length; i++){
           for(var name in this.preview[0]) {
             if (name == this.objName[i][0]){
                  for(var val in  this.preview[0][name]) {
                       this.objName[i].push(this.preview[0][name][val])
                  }
             }

          }
     }
  this.setStrings()
}

第二个将字段中的ID与objName中的数字相匹配,并替换为字符串值

public setStrings(){
  let feildId, feildName;
    for(let i = 0; i < this.fields.length; i++){
      var obj = this.fields[i]
        for(var name in obj) {
          if(this.objName[i][0] == name){
              for(let j = 0; j < obj[name].length; j++){
                feildId = obj[name][j].id
                feildName = obj[name][j].name;
                  for(let x = 0; x < this.objName[i].length; x++){
                    if (this.objName[i][x] == feildId){
                       var index = this.objName[i].indexOf(feildId)
                         if (index !== -1) {
                              this.objName[i][index] = feildName;
                          }

                    }
                  }
              }
          }
        }
    }

console.log(this.objName)
  }

用于输出的objName数组最终看起来像:

[['obj1', 'string_name1', 'string_name2'], ['obj2', 'string_name3', 'string_name4']]

它有效但是让我的眼睛受伤了,必须有一种更容易清洁的方法吗?

Plunker链接: https://plnkr.co/edit/KBDu3ZehHl04er6eut6r?p=preview

2 个答案:

答案 0 :(得分:1)

您的数据结构不适合此类转换。例如,如果显示字符串可以在给定“obj”属性和数组索引的情况下直接寻址,而不必迭代数组,那就更好了。

无论如何,使用现有结构,您仍然可以通过使用数组函数来改进,例如findmap

class App {
    constructor(preview, objName, fields) {
        this.preview = preview;
        this.objName = objName;
        this.fields = fields;
        this.setNumbers();
    }

    setNumbers() {
        this.objName = this.objName.map( arr => arr.concat(this.preview[0][arr[0]]) );
        this.setStrings();
    }

    setStrings() {
        this.objName = this.objName.map( arr =>
            [arr[0]].concat(arr.slice(1).map( val =>
                this.fields.find( field => arr[0] in field )[arr[0]]
                           .find( item => item.id === val ).name
            ))
        );
        console.log(this.objName);
    }
}

var objName = [['obj1'], ['obj2']],
    preview = [{
        obj1: [1, 2],
        obj2: [3, 4]
    }],
    fields = [{
        obj1:[{
            id:1,
            name:'string_name1'
        }, {
            id:2, 
            name:'string_name2'
        }]
    }, {
        obj2:[{
            id:3, 
            name:'string_name3'
        }, {
            id:4, 
            name:'string_name4'
        }]
    }];

new App(preview, objName, fields);
.as-console-wrapper { max-height: 100% !important; top: 0; }

请注意,此代码假定所有搜索都会导致匹配。如果不是这种情况,则必须添加一些代码来定义在不匹配引用的情况下应返回哪些值。​​

以下是代码的变体:

class App {
    constructor(preview, objName, fields) {
        this.preview = preview;
        this.objName = objName;
        this.fields = fields;
        this.setNumbers();
    }

    setNumbers() {
        this.objName = this.objName.map( arr => 
            arr[0] in this.preview[0]
                ? arr.concat(this.preview[0][arr[0]])
                : arr
        );
        this.setStrings();
    }

    setStrings() {
        this.objName = this.objName.map( arr =>
            [arr[0]].concat(arr.slice(1).map( val => {
                let find = this.fields.find( field => arr[0] in field );
                if (find) find = find[arr[0]].find( item => item.id === val );
                return find ? find.name : val;
            }))
        );
        console.log(this.objName);
    }
}

var objName = [['obj1'], ['obj2'], ['obj3']],
    preview = [{
        obj1: [1, 2],
        obj2: [3, 4, 5],
    }],
    fields = [{
        obj1:[{
            id:1,
            name:'string_name1'
        }, {
            id:2, 
            name:'string_name2'
        }]
    }, {
        obj2:[{
            id:3, 
            name:'string_name3'
        }, {
            id:4, 
            name:'string_name4'
        }]
    }];

new App(preview, objName, fields);
.as-console-wrapper { max-height: 100% !important; top: 0; }

答案 1 :(得分:1)

如果你将它分解成更小的部分,这样做会更容易和更清洁:

let objsToMap = [{
  obj1: [1, 2, 7],
  obj2: [3, 4],
  obj3: [1, 2]
}]

let objValues = [{
  obj1: [{
    id: 1,
    name: 'string_name1'
  }, {
    id: 2,
    name: 'string_name2'
  }]
}, {
  obj2: [{
    id: 3,
    name: 'string_name3'
  }, {
    id: 4,
    name: 'string_name4'
  }]
}];

function findValueForId(objDef, id) {
  let idKeyMap = objDef.find(item => item.id === id);

  return idKeyMap ? idKeyMap.name : null;
}

function findObjectValues(valueMapping, key) {
  let objectWithObjectValues = valueMapping.find(item => key in item);

  return objectWithObjectValues ? objectWithObjectValues[key] : null;
}

// returns an array containing key followed by the values corresponding to the specified ids
function lookupObject(key, ids, valueMapping) {
  let objDef = findObjectValues(valueMapping, key) || [];
  let valuesForIds = ids.map(id => findValueForId(objDef, id));
  let valuesWithoutBlanks = valuesForIds.filter(value => value);

  return [key].concat(valuesWithoutBlanks);
}


let result = Object.entries(objsToMap[0]).map(([k, v]) => lookupObject(k, v, objValues));

console.log(result);

您会注意到这种方法在两个地方使用.find(),因为您的第二个数据结构将所有内容嵌入到数组中而不是直接属性引用。这不是很好,因为它对性能不利并且使代码比以前更复杂。

另一种选择是在消耗它之前重新排列第二个数组,这样就像这样:

let objValues = {
  obj1: { 
    '1': 'string_name1',
    '2': 'string_name2'
  },
  obj2: {
    '3': 'string_name3', 
    '4': 'string_name4'
  }
};

以下是你如何做到这一点:

let objsToMap = [{
  obj1: [1, 2, 7],
  obj2: [3, 4],
  obj3: [1, 2]
}]

let objValuesRaw = [{
  obj1: [{
    id: 1,
    name: 'string_name1'
  }, {
    id: 2,
    name: 'string_name2'
  }]
}, {
  obj2: [{
    id: 3,
    name: 'string_name3'
  }, {
    id: 4,
    name: 'string_name4'
  }]
}];

function cleanupObjDef(objDef) {
  return objDef.reduce(function(acc, el) {
    acc[el.id] = el.name;
    return acc;
  }, {});
}

function cleanupObjValues(objValues) {
  let allCombined = Object.assign({}, ...objValues);

  return Object.entries(allCombined).reduce(function (acc, [k, v]) {
    acc[k] = cleanupObjDef(v);
    return acc;
  }, {});
}


// returns an array containing key followed by the values corresponding to the specified ids
function lookupObject(key, ids, valueMapping) {
  let objDef = valueMapping[key] || {};
  let valuesForIds = ids.map(id => objDef[id]);
  let valuesWithoutBlanks = valuesForIds.filter(value => value);

  return [key].concat(valuesWithoutBlanks);
}

let objValues = cleanupObjValues(objValuesRaw);
let result = Object.keys(objsToMap[0]).map(key => lookupObject(key, objsToMap[0][key], objValues));

console.log(result);