Reputation: 305
I have an array of objects, pulled from a CSV file, like so:
const data = [
{ name: 'p1', 'date started': 'April 2007', Houston: '375', Dallas: '508', Austin: '', 'El Paso': '1232' },
{ name: 'p2', 'date started': 'April 2017', Houston: '', Dallas: '', Austin: '', 'El Paso': '43' },
{ name: 'p3', 'date started': 'June 2012', Houston: '18789', Dallas: '', Austin: '8977', 'El Paso': '6754656' },
{ name: 'p4', 'date started': 'December 2015', Houston: '878', Dallas: '4556', Austin: '987', 'El Paso': '1456232' }
]
I am creating some charts and tables of the data, and need to create two new keys, 'location' and 'value', and create new objects for each location value in each object, like this:
const newData = [
{ location: 'Houston', value: '375', name: 'p1', 'date started': 'April 2007' },
{ location: 'Dallas', value: '508', name: 'p1', 'date started': 'April 2007' },
{ location: 'El Paso', value: '1232', name: 'p1', 'date started': 'April 2007' },
{ location: 'El Paso', value: '43', name: 'p2', 'date started': 'April 2017' },
{ location: 'Houston', value: '18789', name: 'p3', 'date started': 'June 2012' },
{ location: 'Austin', value: '8977', name: 'p3', 'date started': 'June 2012' },
{ location: 'El Paso', value: '6754656', name: 'p3', 'date started': 'June 2012' },
{ location: 'Houston', value: '878', name: 'p4', 'date started': 'December 2015' },
{ location: 'Dallas', value: '4556', name: 'p4', 'date started': 'December 2015' },
{ location: 'Austin', value: '987', name: 'p4', 'date started': 'December 2015' },
{ location: 'El Paso', value: '1456232', name: 'p4', 'date started': 'December 2015' }
]
I had to do this for a similar project before, was short on time, and ended up manually editing the original CSV file. I'd rather not do that again. So far I've tried various combinations of map/forEach and Object.keys, with no luck.
Any ideas would be much appreciated!
Upvotes: 3
Views: 140
Reputation: 13376
With an entirely generic approach the OP is in control of all key-value-pairs that have to be assigned "as is" for each newly created data item ... and even for the surrogate keys of each data-entry tuple ...
var data = [
{ name: 'p1', 'date started': 'April 2007', Houston: '375', Dallas: '508', Austin: '', 'El Paso': '1232' },
{ name: 'p2', 'date started': 'April 2017', Houston: '', Dallas: '', Austin: '', 'El Paso': '43' },
{ name: 'p3', 'date started': 'June 2012', Houston: '18789', Dallas: '', Austin: '8977', 'El Paso': '6754656' },
{ name: 'p4', 'date started': 'December 2015', Houston: '878', Dallas: '4556', Austin: '987', 'El Paso': '1456232' }
];
var newData = data.reduce(function (collector, dataItem) {
var
//itemEntryList = Object.entries(dataItem); // if available, otherwise next line ...
itemEntryList = Object.keys(dataItem).map(function (key) {
return [key, dataItem[key]];
}),
assignerList = [],
assignerKey,
idx = -1,
keyForDataKey = collector.keyForDataKey,
keyForDataValue = collector.keyForDataValue,
protectedKeyList = collector.protectedKeyList;
// implement a local `reject` ... for all key value pairs that have to be copied "as is".
while ((assignerKey = itemEntryList[++idx]) && (assignerKey = assignerKey[0])) {
if (protectedKeyList.some(function (protectedKey) {
return (assignerKey === protectedKey);
})) {
assignerList.push({ key: assignerKey, value: itemEntryList[idx][1] });
itemEntryList.splice(idx, 1);
--idx;
}
}
// create new data-item base-structures from the remaining `dataItem` tuples after the `reject` step.
var dataItemList = itemEntryList.reduce(function (itemList, dataTuple) {
var tupleValue = dataTuple[1];
if (tupleValue) {
var newDataItem = {};
newDataItem[keyForDataKey] = dataTuple[0];
newDataItem[keyForDataValue] = tupleValue;
itemList.push(newDataItem);
//itemList.push({ location: dataTuple[0], value: tupleValue });
}
return itemList;
}, []);
// for each new data item ...
dataItemList.map(function (newDataItem) {
return assignerList.reduce(function (dataItem, assignerItem) {
// ... reassign all formerly rejected key value pairs that have to be copied "as is".
dataItem[assignerItem.key] = assignerItem.value;
return dataItem;
}, newDataItem)
});
// collect all new data items.
collector.dataItemList = collector.dataItemList.concat(dataItemList);
return collector;
}, {
keyForDataKey: 'location',
keyForDataValue: 'value',
protectedKeyList: ['name', 'date started'],
dataItemList: []
}).dataItemList;
console.log('data : ', data);
console.log('newData : ', newData);
.as-console-wrapper { max-height: 100%!important; top: 0; }
Upvotes: 1
Reputation: 2537
This becomes pretty clean with destructuring assignment and rest/spread syntax.
const data = [
{ name: 'p1', 'date started': 'April 2007', Houston: '375', Dallas: '508', Austin: '', 'El Paso': '1232' },
{ name: 'p2', 'date started': 'April 2017', Houston: '', Dallas: '', Austin: '', 'El Paso': '43' },
{ name: 'p3', 'date started': 'June 2012', Houston: '18789', Dallas: '', Austin: '8977', 'El Paso': '6754656' },
{ name: 'p4', 'date started': 'December 2015', Houston: '878', Dallas: '4556', Austin: '987', 'El Paso': '1456232' }
];
const result = data.reduce((res, obj) => {
const date_started = obj["date started"];
delete obj["date started"];
const {name, ...rest} = obj;
return [...res, ...Object.entries(rest).map(([k,v]) =>
({location:k, value:v, name:name, 'date started':date_started})
)]
}, []);
console.log(result);
If you wish to avoid the rest parameter in object literals, and you don't want the empty locations, you can create a Set
that is used to exclude the non-location keys, and use truthy evaluation to exclude the undesired locations.
var data = [{ name: 'p1', 'date started': 'April 2007', Houston: '375', Dallas: '508', Austin: '', 'El Paso': '1232' }, { name: 'p2', 'date started': 'April 2017', Houston: '', Dallas: '', Austin: '', 'El Paso': '43' }, { name: 'p3', 'date started': 'June 2012', Houston: '18789', Dallas: '', Austin: '8977', 'El Paso': '6754656' }, { name: 'p4', 'date started': 'December 2015', Houston: '878', Dallas: '4556', Austin: '987', 'El Paso': '1456232' }];
var exc = new Set(['name', 'date started']);
var result = data.reduce((r, o) =>
[
...r,
...Object.entries(o)
.filter(([k, v]) => v && !exc.has(k))
.map(([k, v]) => ({ location: k, value: v, name: o.name, 'date started': o['date started'] }))
]
, []);
console.log(result, null, 2);
Upvotes: 1
Reputation: 386560
You could iterate all keys of the object and exclude the unwanted propoerties and build new objects for the result set.
var data = [{ name: 'p1', 'date started': 'April 2007', Houston: '375', Dallas: '508', Austin: '', 'El Paso': '1232' }, { name: 'p2', 'date started': 'April 2017', Houston: '', Dallas: '', Austin: '', 'El Paso': '43' }, { name: 'p3', 'date started': 'June 2012', Houston: '18789', Dallas: '', Austin: '8977', 'El Paso': '6754656' }, { name: 'p4', 'date started': 'December 2015', Houston: '878', Dallas: '4556', Austin: '987', 'El Paso': '1456232' }],
result = data.reduce(function (r, o) {
Object.keys(o).forEach(function (k) {
if (['name', 'date started'].includes(k) || !o[k]) {
return;
}
r.push({ location: k, value: o[k], name: o.name, 'date started': o['date started'] });
});
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Upvotes: 2
Reputation: 86
You could iterate through all items
which are your old objects and then iterate through all keys in the old object and build a new array based on that, like so
const data = [
{ name: 'p1', 'date started': 'April 2007', Houston: '375', Dallas: '508', Austin: '', 'El Paso': '1232' },
{ name: 'p2', 'date started': 'April 2017', Houston: '', Dallas: '', Austin: '', 'El Paso': '43' },
{ name: 'p3', 'date started': 'June 2012', Houston: '18789', Dallas: '', Austin: '8977', 'El Paso': '6754656' },
{ name: 'p4', 'date started': 'December 2015', Houston: '878', Dallas: '4556', Austin: '987', 'El Paso': '1456232' }
];
function transform() {
var newObjects = [];
data.forEach(item => {
Object.keys(item).forEach(keyName => {
if (keyName !== "name" && keyName !== "date started") {
newObjects.push({
location: keyName,
value: item[keyName],
["date started"]: item["date started"],
name: item["name"]
})
}
});
});
return newObjects;
}
console.log(transform(data));
Hope this helps
Upvotes: 1