如何使用普通 Javascript 访问双层嵌套 arrays/objects 的最内层?

How to access innermost levels of doubly nested arrays/objects using plain Javascript?

我想答案很简单,但我已经搜索过这个网站和其他网站,但没有找到。我有一个双层嵌套的数据结构,不知道如何遍历最内层。我猜它可能涉及 forEach() 或 map() 方法,但我没有尝试过。

背景: 我已经简化了这个问题的数据。数据(粘贴在下面)存储在一个包含 2 个零售店对象的数组中。每个商店对象都有一个 visits 属性 ,其值是一个访问对象数组。每次访问(对象)都由访问日期标识(假设在日期 B 最多可以有 1 次访问商店 A)。每个访问对象都包含一个 values 属性,其值是访问时进行的一组交易(购买或 returns)。在实际的全量数据中,每个商店每个日期的交易数量变化很大。

我需要帮助的任务: (a) 将 属性 key 重命名为 visitDate, (b) 将 属性 values 重命名为 transactions, (c) 删除 8 个冗余属性(从 storeIDstoreVisitDate)但保留 actiondollarAmount 属性,以及 (d) 将 属性 dollarAmount 重命名为 dollars.

如有任何帮助,我们将不胜感激。谢谢。

 [
   {
     "storeName": "Ye Olde Candy Shoppe",
     "address": "1313 Vampire Lane, Cityville NY 99999",
     "zipCode": "99999",
     "storeSize": "large",
     "visits": [
       {
         "key": "5/3/12",
         "values": [
           {
             "storeID": "53454447",
             "storeName": "Ye Olde Candy Shoppe",
             "city": "Cityville",
             "building": "1313",
             "street": "Vampire Lane",
             "zipcode": "99999",
             "storeSize": "large",
             "storeVisitDate": "5/3/12",
             "action": "Return",
             "dollarAmount": "65.43"
           },
           {
             "storeID": "53454447",
             "storeName": "Ye Olde Candy Shoppe",
             "city": "Cityville",
             "building": "1313",
             "street": "Vampire Lane",
             "zipcode": "99999",
             "storeSize": "large",
             "storeVisitDate": "5/3/12",
             "action": "Purchase",
             "dollarAmount": "12.43"
           },
           {
             "storeID": "53454447",
             "storeName": "Ye Olde Candy Shoppe",
             "city": "Cityville",
             "building": "1313",
             "street": "Vampire Lane",
             "zipcode": "99999",
             "storeSize": "large",
             "storeVisitDate": "5/3/12",
             "action": "Purchase",
             "dollarAmount": "5.43"
           }
         ]
       },
       {
         "key": "12/31/12",
         "values": [
           {
             "storeID": "53454447",
             "storeName": "Ye Olde Candy Shoppe",
             "city": "Cityville",
             "building": "1313",
             "street": "Vampire Lane",
             "zipcode": "99999",
             "storeSize": "large",
             "storeVisitDate": "12/31/12",
             "action": "Purchase",
             "dollarAmount": "2.53"
           }
         ]
       },
       {
         "key": "1/24/13",
         "values": [
           {
             "storeID": "53454447",
             "storeName": "Ye Olde Candy Shoppe",
             "city": "Cityville",
             "building": "1313",
             "street": "Vampire Lane",
             "zipcode": "99999",
             "storeSize": "large",
             "storeVisitDate": "1/24/13",
             "action": "Return",
             "dollarAmount": "2.53"
           },
           {
             "storeID": "53454447",
             "storeName": "Ye Olde Candy Shoppe",
             "city": "Cityville",
             "building": "1313",
             "street": "Vampire Lane",
             "zipcode": "99999",
             "storeSize": "large",
             "storeVisitDate": "1/24/13",
             "action": "Return",
             "dollarAmount": "64.22"
           }
         ]
       }
     ]
   },
   {
     "storeName": "Mike's Bikes",
     "address": "2626 Aardvark Circle, Townsville NY 88888",
     "zipCode": "88888",
     "storeSize": "small",
     "visits": [
       {
         "key": "8/8/14",
         "values": [
           {
             "storeID": "24335234",
             "storeName": "Mike's Bikes",
             "city": "Townsville",
             "building": "2626",
             "street": "Aardvark Circle",
             "zipcode": "88888",
             "storeSize": "small",
             "storeVisitDate": "8/8/14",
             "action": "Purchase",
             "dollarAmount": "443.55"
           },
           {
             "storeID": "24335234",
             "storeName": "Mike's Bikes",
             "city": "Townsville",
             "building": "2626",
             "street": "Aardvark Circle",
             "zipcode": "88888",
             "storeSize": "small",
             "storeVisitDate": "8/8/14",
             "action": "Purchase",
             "dollarAmount": "34"
           },
           {
             "storeID": "24335234",
             "storeName": "Mike's Bikes",
             "city": "Townsville",
             "building": "2626",
             "street": "Aardvark Circle",
             "zipcode": "88888",
             "storeSize": "small",
             "storeVisitDate": "8/8/14",
             "action": "Purchase",
             "dollarAmount": "12.32"
           }
         ]
       },
       {
         "key": "10/3/15",
         "values": [
           {
             "storeID": "24335234",
             "storeName": "Mike's Bikes",
             "city": "Townsville",
             "building": "2626",
             "street": "Aardvark Circle",
             "zipcode": "88888",
             "storeSize": "small",
             "storeVisitDate": "10/3/15",
             "action": "Purchase",
             "dollarAmount": "233.1"
           },
           {
             "storeID": "24335234",
             "storeName": "Mike's Bikes",
             "city": "Townsville",
             "building": "2626",
             "street": "Aardvark Circle",
             "zipcode": "88888",
             "storeSize": "small",
             "storeVisitDate": "10/3/15",
             "action": "Return",
             "dollarAmount": "44.99"
           }
         ]
       }
     ]
   }
 ]

要重命名属性,您可以创建一个与 delete 旧属性具有相同值的新属性。

您可以将它用于所有部分,但查看您的结构,通过使用 map() 创建新的 "optimised" 事务数组将 b、c、d 组合在一起看起来更整洁,并且将其分配给新的 transactions 属性.

// (I put this in a function so the logic can be at the top of the snippet)
function fixData(stores) {
  // loop stores and their visits
  for (var iStore = 0; iStore < stores.length; iStore++) {
    var store = stores[iStore];

    for (var iVisit = 0; iVisit < store.visits.length; iVisit++) {
      var visit = store.visits[iVisit];

      // (a) rename property key to visitDate
      //   add a new property with the same value then delete the old property
      visit.visitDate = visit.key;
      delete visit.key;

      // (b) rename property values to transactions
      //   add a new property with the same value then delete the old property
      // (c) delete the 8 redundant properties (from storeID to storeVisitDate)
      //   we could delete keys but quicker to map a new object
      // (d) rename property dollarAmount to dollars.
      //   just give the new object property a different name
      visit.transactions = visit.values.map(function(trans) {
        return {
          action: trans.action,
          dollars: trans.dollarAmount
        }
      });
      delete visit.values;
    }
  }

  console.log(stores);
}


var stores = [{
  "storeName": "Ye Olde Candy Shoppe",
  "address": "1313 Vampire Lane, Cityville NY 99999",
  "zipCode": "99999",
  "storeSize": "large",
  "visits": [{
    "key": "5/3/12",
    "values": [{
      "storeID": "53454447",
      "storeName": "Ye Olde Candy Shoppe",
      "city": "Cityville",
      "building": "1313",
      "street": "Vampire Lane",
      "zipcode": "99999",
      "storeSize": "large",
      "storeVisitDate": "5/3/12",
      "action": "Return",
      "dollarAmount": "65.43"
    }, {
      "storeID": "53454447",
      "storeName": "Ye Olde Candy Shoppe",
      "city": "Cityville",
      "building": "1313",
      "street": "Vampire Lane",
      "zipcode": "99999",
      "storeSize": "large",
      "storeVisitDate": "5/3/12",
      "action": "Purchase",
      "dollarAmount": "12.43"
    }, {
      "storeID": "53454447",
      "storeName": "Ye Olde Candy Shoppe",
      "city": "Cityville",
      "building": "1313",
      "street": "Vampire Lane",
      "zipcode": "99999",
      "storeSize": "large",
      "storeVisitDate": "5/3/12",
      "action": "Purchase",
      "dollarAmount": "5.43"
    }]
  }, {
    "key": "12/31/12",
    "values": [{
      "storeID": "53454447",
      "storeName": "Ye Olde Candy Shoppe",
      "city": "Cityville",
      "building": "1313",
      "street": "Vampire Lane",
      "zipcode": "99999",
      "storeSize": "large",
      "storeVisitDate": "12/31/12",
      "action": "Purchase",
      "dollarAmount": "2.53"
    }]
  }, {
    "key": "1/24/13",
    "values": [{
      "storeID": "53454447",
      "storeName": "Ye Olde Candy Shoppe",
      "city": "Cityville",
      "building": "1313",
      "street": "Vampire Lane",
      "zipcode": "99999",
      "storeSize": "large",
      "storeVisitDate": "1/24/13",
      "action": "Return",
      "dollarAmount": "2.53"
    }, {
      "storeID": "53454447",
      "storeName": "Ye Olde Candy Shoppe",
      "city": "Cityville",
      "building": "1313",
      "street": "Vampire Lane",
      "zipcode": "99999",
      "storeSize": "large",
      "storeVisitDate": "1/24/13",
      "action": "Return",
      "dollarAmount": "64.22"
    }]
  }]
}, {
  "storeName": "Mike's Bikes",
  "address": "2626 Aardvark Circle, Townsville NY 88888",
  "zipCode": "88888",
  "storeSize": "small",
  "visits": [{
    "key": "8/8/14",
    "values": [{
      "storeID": "24335234",
      "storeName": "Mike's Bikes",
      "city": "Townsville",
      "building": "2626",
      "street": "Aardvark Circle",
      "zipcode": "88888",
      "storeSize": "small",
      "storeVisitDate": "8/8/14",
      "action": "Purchase",
      "dollarAmount": "443.55"
    }, {
      "storeID": "24335234",
      "storeName": "Mike's Bikes",
      "city": "Townsville",
      "building": "2626",
      "street": "Aardvark Circle",
      "zipcode": "88888",
      "storeSize": "small",
      "storeVisitDate": "8/8/14",
      "action": "Purchase",
      "dollarAmount": "34"
    }, {
      "storeID": "24335234",
      "storeName": "Mike's Bikes",
      "city": "Townsville",
      "building": "2626",
      "street": "Aardvark Circle",
      "zipcode": "88888",
      "storeSize": "small",
      "storeVisitDate": "8/8/14",
      "action": "Purchase",
      "dollarAmount": "12.32"
    }]
  }, {
    "key": "10/3/15",
    "values": [{
      "storeID": "24335234",
      "storeName": "Mike's Bikes",
      "city": "Townsville",
      "building": "2626",
      "street": "Aardvark Circle",
      "zipcode": "88888",
      "storeSize": "small",
      "storeVisitDate": "10/3/15",
      "action": "Purchase",
      "dollarAmount": "233.1"
    }, {
      "storeID": "24335234",
      "storeName": "Mike's Bikes",
      "city": "Townsville",
      "building": "2626",
      "street": "Aardvark Circle",
      "zipcode": "88888",
      "storeSize": "small",
      "storeVisitDate": "10/3/15",
      "action": "Return",
      "dollarAmount": "44.99"
    }]
  }]
}];

fixData(stores);

如果我们假设你的数组叫做arr,我建议:

arr.forEach(function(currentValue, index, array) {
   currentValue.visits = currentValue.visits.map(function(currentValue, index, array) {
      currentValue.visitDate = currentValue.key;
      delete currentValue.key;
      currentValue.transactions = currentValue.values.map(function(currentValue, index, array) {
         currentValue = {action: currentValue.action, dollars: currentValue.dollarAmount};
         return currentValue;
      });
      delete currentValue.values;
      return currentValue;
   });
});

好吧,因为这是 'data' 而不是一些内存中的对象,我猜你是通过使用 JSON.parse 从 JSON 得到的。 (如果没有,您仍然可以通过先使用 JSON.strigify 来使用此方法)

你知道JSON.parse接受一个函数来控制这些东西吗? https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse

所以....

fixed=JSON.parse(dataString, function(key,value){
 if(key=="key"){this.visitDate=value;return}
 if(key=="values"){this.transactions=value;return}
 if(key=="somethingYouDontWant"){return;}
 //etc...
 return value;    
})

它是纯粹的 javascript,没有任何库,使用简单的地图:

 var y = x.map(function(xs){
      xs.visits = xs.visits.map(function (item){
        return {
          visitDate: item.key,
          transactions: item.values.map(function(v){
            return {
              action: v.action,
              dollars: v.dollarAmount
            };
          })
        };
      });
      return xs;
    });

使用 Array.map()delete 运算符的灵活解决方案:

var delete_props = ["storeID","storeName","city","building","street", "zipcode","storeSize", "storeVisitDate"];

 // arr is your initial array
 arr.map(function(obj){
     obj['visits'].map(function(inner_obj){
         inner_obj['visitDate'] = inner_obj['key'];
         delete inner_obj['key'];

         inner_obj['values'].map(function(values_obj){
            values_obj['dollars'] = values_obj['dollarAmount'];
            delete values_obj['dollarAmount'];

            delete_props.forEach(function(v){
                delete values_obj[v];
            });
        });
        inner_obj['transactions '] = inner_obj['values'];
        delete inner_obj['values'];

     });     
 });

这是另一个解决方案,代码非常简单但没有优化。

var obj = {}, key;
data.forEach(item => {
  item.visits.forEach(visit => {
    visit.visitDate = visit.key; 
    delete visit.key;
    visit.transactions = [];
    visit.values.forEach(value => {
      obj = {};
      for (key in value) {
        if (value.hasOwnProperty(key) && retain.indexOf(key) > -1) {
          obj[key] = value[key];
        }
      }
      visit.transactions.push(obj);
    });
    delete visit.values;
  });
console.log(item);
});

你说得对!您可以使用 .map() 完成大部分工作。而新的 ES6 标准让这一切变得更容易,使用下面的函数你甚至不会修改任何原始数据!:

array.map(store => {
  //return a new object that takes all the store info, then reassigns the visits key in a new object
  return Object.assign({}, store, {
    //map over visits, and reassign the key key to visitDate
    visits: store.visits.map(({ key: visitDate, values }) => {
      return {
        //return an obj with visit date
        visitDate,
        // do destructuring again to create objects of action,dollars
        transactions: values.map(({ action, dollarAmount: dollars }) => ({ action, dollars }))
      };
    })
  });
});

(working example on jsFiddle here - 打开JS控制台就可以看到转换后的数据集)

关于以下解决方案的几点:

  • 即使您的数据具有比您粘贴的示例中更多的键,它也会起作用(您提到这是一个简化的集合,因此它可能很重要)
  • 它经常使用 "map",而不是手动遍历数组。我发现它更具可读性。

    // Go over all stores
    stores.map(function(store) {
      // In each store, go over all visits.
      store.visits.map(function(visit) {
        // In each visit, copy 'key' to 'visitDate'
        // and 'values' to 'transactions'.
        // Then delete old names ('key' and 'values').
        visit.visitDate = visit.key;
        visit.transactions = visit.values;
        delete visit.key;
        delete visit.values;
    
        // For each transaction, replace it with a simple
        // map with only 'action' and 'dollars'.
        visit.transactions = visit.transactions.map(function(tx) {
          return {
            action: tx.action,
            dollars: tx.dollarAmount
          };
        });
      });
    });
    

请注意 map() 受 IE9 及更新版本支持。