I suspect the answer is simple but I've searched this site and others and haven't found one. I have a doubly nested data structure and can't figure out how to iterate over the innermost levels. I'm guessing it might involve the forEach() or map() method but nothing I've tried works.
Background: I've simplified the data for this question. The data (pasted below) are stored in an array containing 2 retail store objects. Each store object has a visits
property whose value is an array of visit objects. Each visit (object) is identified by a visit date (assume there can be at most 1 visit to store A on date B). Each visit object contains a values
property whose value is an array of transactions (purchases or returns) made at the visit. In the actual full data, the number of transactions per date per store is highly variable.
Tasks I need help with: (a) rename property key
to visitDate
, (b) rename property values
to transactions
, (c) delete the 8 redundant properties (from storeID
to storeVisitDate
) but retain the action
and dollarAmount
properties, and (d) rename property dollarAmount
to dollars
.
Any help would be most appreciated. Thanks.
[
{
"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"
}
]
}
]
}
]
You're correct! You can do most of this with .map(). And the new ES6 standard makes a lot of this much easier, and with the below function you wont even modify any of the original data!:
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 - just open JS console to see the converted data set)
Few points about solution below:
it uses "map" a lot, instead of manually iterating over arrays. I find it more readable.
// 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 }; }); }); });
Note that map() is supported in IE9 and newer.
To rename properties, you can create a new one with the same value then delete
the old one.
You can use this for all parts, but looking at your structure, it looks neater to combine b,c,d together by using map()
to create a new array of "optimised" transactions and assigning that to the new transactions
property.
// (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);
If we assume your array is called arr, I propose:
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; }); });
Well, since this is 'data' rather than some in-memory objects, I'm guessing you got it from JSON by using JSON.parse
. (If not, you can still use this approach by first using JSON.strigify
)
Did you know JSON.parse
accepts an function to control such things? https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse
So....
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;
})
Its pure javascript without any library and uses simple map:
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;
});
Flexible solution with Array.map()
and delete
operator:
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'];
});
});
here is another solution, the code is pretty straightforward but not optimized.
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);
});
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.