I have an object array (coming from an XLSX.js parser, so its length and contents vary) representing grants that have been given to projects.
Simplified, it looks something like this:
var grants = [
{ id: "p_1", location: "loc_1", type: "A", funds: "5000" },
{ id: "p_2", location: "loc_2", type: "B", funds: "2000" },
{ id: "p_3", location: "loc_3", type: "C", funds: "500" },
{ id: "p_2", location: "_ibid", type: "D", funds: "1000" },
{ id: "p_2", location: "_ibid", type: "E", funds: "3000" }
];
I need to merge these into a new array that will look like this:
var projects = [
{ id: "p_1", location: "loc_1", type: "A", funds: "5000" },
{ id: "p_2", location: "loc_2", type: ["B", "D", "E"], funds: ["2000", "1000", "3000"] },
{ id: "p_3", location: "loc_3", type: "C", funds: "500" }
];
... so that when the id is the same, it will merge the objects and combine some of their key values (in the example type and funds) into a simple sub-array. The other keys (location) in these merged objects inherit the values from the first instance and ignore the rest.
After several failed attempts and a lot of searching online, I got an idea from this answer to loop through grants like this:
var res = {};
$.each(grants, function (key, value) {
if (!res[value.id]) {
res[value.id] = value;
} else {
res[value.id].type = [res[value.id].type, value.type];
res[value.id].funds = [res[value.id].funds, value.funds];
}
});
var projects = []
projects = $.map( res, function (value) { return value; } );
It actually works perfectly, EXCEPT that as I need an array, I removed .join(',') from the ends (from the answer mentioned above), which in turn has created the problem I can't seem to solve now. The sub-arrays become nested in each other somehow if there is at least three items in them! I sort of understand why (the loop), but I wonder if there is a way to convert all these little multi-dimensional arrays inside the objects into sigle arrays (like: type: ["B", "D", "E"] )?
var grants = [
{ id: "p_1", location: "loc_1", type: "A", funds: "5000" },
{ id: "p_2", location: "loc_2", type: "B", funds: "2000" },
{ id: "p_3", location: "loc_3", type: "C", funds: "500" },
{ id: "p_2", location: "_ibid", type: "D", funds: "1000" },
{ id: "p_2", location: "_ibid", type: "E", funds: "3000" }
];
var res = {};
$.each(grants, function (key, value) {
if (!res[value.id]) {
res[value.id] = value;
} else {
res[value.id].type = [res[value.id].type, value.type];
res[value.id].funds = [res[value.id].funds, value.funds];
}
});
var projects = []
projects = $.map( res, function (value) { return value; } );
$("pre").html(JSON.stringify(projects,null,2));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<pre id="json"></pre>
4 Answers 4
Would this be an idea?
var grants = [
{ id: "p_1", location: "loc_1", type: "A", funds: "5000" },
{ id: "p_2", location: "loc_2", type: "B", funds: "2000" },
{ id: "p_3", location: "loc_3", type: "C", funds: "500" },
{ id: "p_2", location: "_ibid", type: "D", funds: "1000" },
{ id: "p_2", location: "_ibid", type: "E", funds: "3000" }
];
var joined = [];
// map and push to joined
grants.map(
function (v) {
if (!(v.id in this)) {
this[v.id] = v;
joined.push(v);
} else {
var current = this[v.id];
current.type = [v.type].concat(current.type);
current.funds = [v.funds].concat(current.funds);
}
}, {}
);
// show it
document.querySelector('#result').textContent =
JSON.stringify(joined, null, ' ');
<pre id="result"></pre>
3 Comments
You can just change these lines:
res[value.id].type = [res[value.id].type, value.type];
res[value.id].funds = [res[value.id].funds, value.funds];
To this:
Array.isArray(res[value.id].type) ? res[value.id].type.push(value.type) : res[value.id].type = [res[value.id].type, value.type];
Array.isArray(res[value.id].funds) ? res[value.id].funds.push(value.funds) : res[value.id].funds = [res[value.id].funds, value.funds];
1 Comment
This will do:
var tempArr = [];
var result = [];
for(i in grants){
var rowObj = grants[i];
var idPos = tempArr.indexOf(rowObj.id);
if(idPos > -1){
result[idPos].type.push(rowObj.type);
result[idPos].funds.push(rowObj.funds);
}else{
tempArr.push(rowObj.id);
rowObj.type = [rowObj.type]
rowObj.funds = [rowObj.funds]
result.push(rowObj);
}
}
console.log(result);
1 Comment
I propose this solution.
It features a look up, if the index exist in the project array, if so it pushes the type and funds if not then the type and funds properties are changed to array with the value as first element.
var grants = [
{ id: "p_1", location: "loc_1", type: "A", funds: "5000" },
{ id: "p_2", location: "loc_2", type: "B", funds: "2000" },
{ id: "p_3", location: "loc_3", type: "C", funds: "500" },
{ id: "p_2", location: "_ibid", type: "D", funds: "1000" },
{ id: "p_2", location: "_ibid", type: "E", funds: "3000" }
],
project = [];
grants.forEach(function (a) {
!project.some(function (b, i) {
if (a.id === b.id) {
project[i].type.push(a.type);
project[i].funds.push(a.funds);
return true;
}
}) && project.push({ id: a.id, location: a.location, type: [a.type], funds: [a.funds] });
});
document.write('<pre>' + JSON.stringify(project, 0, 4) + '</pre>');
1 Comment
Explore related questions
See similar questions with these tags.