Reputation: 1376
I am trying to take data from one array, group the data by object property, and then send it to a new array. The current data format is as such:
const data = [
{
"dataId": "1",
"tableName": "table1",
"column": "firstHeader",
"rows": [
"a","b","c"
]
},
{
"dataId": "2",
"tableName": "table1",
"column": "secondHeader",
"rows": [
"d","e","f",
]
},
{
"dataId": "3",
"tableName": "table2",
"column": "aNewFirstHeader",
"rows": [
1,2,3
]
}
];
I used the Lodash groupby method, which groups the items in a format that is close to what I am expecting, but not exactly what I want my end result to be. The lodash result is:
[
[
{ "tableName": 'table1',
"dataId": '1',
"header": 'firstHeader',
"rows": ["a","b","c"]
},
{ "tableName": 'table1',
"dataId": '1',
"header": 'secondHeader',
"rows": ["d","e","f"]
}
],
[
{ "tableName": 'table2',
"dataId": '2',
"header": 'aNewFirstHeader',
"rows": [1,2,3] },
]
]
Ultimately, what I am looking for is the end result of this:
[
{
"tableName": "table1",
"column": ["firstHeader", "secondHeader"],
"rows": [["a","b","c"], ["d","e","f"]]
},
{
"tableName": "table2",
"column": ["aNewFirstHeader"],
"rows": [[1,2,3]]
}
]
Is there a different way to use the groupBy method, or perhaps a javascript only approach that would get the end result I am looking for?
Upvotes: 2
Views: 923
Reputation: 1
Using ES6 with unique key approach:
"Way of the future" :-)
const desiredResults = [{
"dataId": "1",
"tableName": "table1",
"column": "firstHeader",
"rows": [
"a", "b", "c"
]
},
{
"dataId": "2",
"tableName": "table1",
"column": "secondHeader",
"rows": [
"d", "e", "f",
]
},
{
"dataId": "3",
"tableName": "table2",
"column": "aNewFirstHeader",
"rows": [
1, 2, 3
]
}
]
.reduce((unqiueMap, {
dataId, // Destructuring each data element
tableName,
column,
rows
}) => {
if (tableName in unqiueMap) {
unqiueMap[tableName].column.push(column);
unqiueMap[tableName].rows.push(rows);
} else {
unqiueMap[tableName] = {
column: [column],
rows: [rows]
}
}
return unqiueMap;
}, {})
console.log(desiredResults)
Upvotes: 0
Reputation: 566
Edit: Oh, I totally forgot that .findIndex()
is a thing. Here is the updated solution:
Use .map()
, .reduce()
& .findIndex()
:
const newArr = data.map(value => ({
tableName: value.tableName,
column: [value.column],
rows: [value.rows],
}))
.reduce((acc, cur) => {
const tableNameIndex = acc.findIndex(el => el.tableName === cur.tableName);
if (tableNameIndex !== -1) {
acc[tableNameIndex].column.push(cur.column[0]);
acc[tableNameIndex].rows.push(cur.rows[0]);
return acc;
} else {
acc.push(cur);
return acc;
}
}, [])
console.log(newArr);
Upvotes: 0
Reputation: 386560
In plain Javascript, you could use Map
var data = [{ dataId: "1", tableName: "table1", column: "firstHeader", rows: ["a", "b", "c"] }, { dataId: "2", tableName: "table1", column: "secondHeader", rows: ["d", "e", "f", ] }, { dataId: "3", tableName: "table2", column: "aNewFirstHeader", rows: [1, 2, 3] }],
result = data.reduce((map => (r, a) => {
var item = map.get(a.tableName);
if (!item) {
item = { tableName: a.tableName, column: [], rows: [] }
map.set(a.tableName, item);
r.push(item);
}
item.column.push(a.column);
item.rows.push(a.rows);
return r;
})(new Map), []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Upvotes: 0
Reputation: 2888
Plain Javascript:
function groupBy(data, fieldName) {
var groups = {}
for (var i=0; i<data.length; i++) {
var elem = data[i];
if (fieldName in elem) {
var fieldVal = elem[fieldName];
if (!(fieldVal in groups)) {
groups[fieldVal] = [];
}
groups[fieldVal].push(elem)
}
}
var result = [];
for (list in groups) {
var arr = groups[list];
var combine = {}
combine[fieldName] = arr[0][fieldName];
for (var i=0; i<arr.length; i++) {
var elem = arr[i];
for (prop in elem) {
if (prop == fieldName) {
continue;
}
if (!(prop in combine)) {
combine[prop] = [];
}
combine[prop].push(elem[prop]);
}
}
result.push(combine);
}
return result;
}
For your case, simply invoke:
groupBy(data, "tableName")
Upvotes: 0
Reputation: 191976
After using _.groupBy()
map each group using _.mergeWith()
to combine all objects in the group to a new object, and push everything, except tableName
into an array. Then use _.omit()
to remove dataId
:
const data = [{"dataId":"1","tableName":"table1","column":"firstHeader","rows":["a","b","c"]},{"dataId":"2","tableName":"table1","column":"secondHeader","rows":["d","e","f"]},{"dataId":"3","tableName":"table2","column":"aNewFirstHeader","rows":[1,2,3]}];
const result = _(data)
.groupBy('tableName')
.map((group) => // map each group
// remove the dataId property
_.omit(
// merge all group objects
_.mergeWith({}, ...group, (objValue = [], srcValue, key) => {
// if the key is not tableName push it into the array
if (key !== 'tableName') {
objValue.push(srcValue);
return objValue;
}
}),
'dataId'))
.value();
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
Upvotes: 0
Reputation: 1310
By using reduce
and findIndex
, we can rebuild a new array which suits your format in O(n) time.
data.reduce((arr, record) => {
const index = arr.findIndex((inside) => (inside.tableName === record.tableName));
if (index === -1) {
arr.push({
tableName: record.tableName,
column: [record.column],
rows: [record.rows]
})
} else {
arr[index].column.push(record.column);
arr[index].rows.push(record.rows);
}
return arr;
}, []);
const data = [
{
"dataId": "1",
"tableName": "table1",
"column": "firstHeader",
"rows": [
"a", "b", "c"
]
},
{
"dataId": "2",
"tableName": "table1",
"column": "secondHeader",
"rows": [
"d", "e", "f",
]
},
{
"dataId": "3",
"tableName": "table2",
"column": "aNewFirstHeader",
"rows": [
1, 2, 3
]
}
];
const newData = data.reduce((arr, record) => {
const index = arr.findIndex((inside) => (inside.tableName === record.tableName));
if (index === -1) {
arr.push({
tableName: record.tableName,
column: [record.column],
rows: [record.rows]
})
} else {
arr[index].column.push(record.column);
arr[index].rows.push(record.rows);
}
return arr;
}, []);
console.log(newData);
Upvotes: 2
Reputation: 18763
const data = [
{
"dataId": "1",
"tableName": "table1",
"column": "firstHeader",
"rows": [
"a","b","c"
]
},
{
"dataId": "2",
"tableName": "table1",
"column": "secondHeader",
"rows": [
"d","e","f",
]
},
{
"dataId": "3",
"tableName": "table2",
"column": "aNewFirstHeader",
"rows": [
1,2,3
]
}
];
var result = _.chain(data)
.groupBy("tableName")
.pairs()
.map(function (currentItem) {
// used to create desired properties
var rows = [];
var columns = [];
// loop to fill values
for(var i=0,length=currentItem[1].length;i<length;i++){
columns.push(currentItem[1][i].column)
rows = rows.concat(currentItem[1][i].rows);
}
return {
'tableName': currentItem[0],
'column': columns,
'rows': rows
};
})
.value();
console.log(result);
Upvotes: 0
Reputation: 4020
Try this , used plain vanilla js only:
var merge = function( data){
var outputArray = [], enteredNames = [];
for(var i=0; i<data.length; i++){
var datum = data[i];
var ownElement;
if(enteredNames.indexOf(datum.tableName)>-1)
ownElement = outputArray[enteredNames.indexOf(datum.tableName)];
else
{
ownElement = {
"tableName": datum.tableName,
"column": [],
"rows": []
}
enteredNames.push( datum.tableName );
outputArray.push( ownElement );
}
ownElement.column.push( datum.column );
ownElement.rows.push( datum.rows );
}
return outputArray;
}
Upvotes: 0
Reputation: 1524
Use the built-in JavaScript Array Prototype methods map
and reduce
to simplify your problem:
const data = [
{
"dataId": "1",
"tableName": "table1",
"column": "firstHeader",
"rows": [
"a", "b", "c"
]
},
{
"dataId": "2",
"tableName": "table1",
"column": "secondHeader",
"rows": [
"d", "e", "f",
]
},
{
"dataId": "3",
"tableName": "table2",
"column": "aNewFirstHeader",
"rows": [
1, 2, 3
]
}
];
const EXPECTED = [
{
"tableName": "table1",
"column": ["firstHeader", "secondHeader"],
"rows": [["a", "b", "c"], ["d", "e", "f"]]
},
{
"tableName": "table2",
"column": ["aNewFirstHeader"],
"rows": [[1, 2, 3]]
}
];
var result = data
.reduce((hash, dataItem) => {
// first group by tableName
if (hash[dataItem.tableName]) {
hash[dataItem.tableName].column.push(dataItem.column);
hash[dataItem.tableName].rows.push(dataItem.rows);
} else {
hash[dataItem.tableName] = {
column: [dataItem.column],
rows: [dataItem.rows]
};
}
return hash;
}, {});
// run a map to change to the exact array style you want
result = Object.keys(result).map((tableName) => {
return {
tableName: tableName,
column: result[tableName].column,
rows: result[tableName].rows
};
});
// if in a node environment, you can assert (for unit testing only)
require('assert').deepStrictEqual(result, EXPECTED);
Upvotes: 0