I'm trying to find the simplest way to merge this dynamic array using jQuery.
This is the result I'm getting after trying to use .reduce. The Month/Year object is dynamic, the result is based on the date range.
[
{
"ObjectGroupingId": 1,
"Jan 2022": "Project1",
"Feb 2022": 0,
"Mar 2022": 0,
"Apr 2022": 0,
"May 2022": 0
},
{
"ObjectGroupingId": 1,
"Jan 2022": 0,
"Feb 2022": "Project2",
"Mar 2022": 0,
"Apr 2022": 0,
"May 2022": 0
},
{
"ObjectGroupingId": 1,
"Jan 2022": 0,
"Feb 2022": 0,
"Mar 2022": "Project3",
"Apr 2022": 0,
"May 2022": 0
}
]
What I'm trying to achieve is this
[
{
"ObjectGroupingId": 1,
"Jan 2022": "Project1",
"Feb 2022": "Project2",
"Mar 2022": "Project3"
}
]
This is what I've tried in jQuery
var monthNames = data
.map(function (t) {
var monthName = formatMonthYear(t.dynamicHeaderColumn);
return monthName;
})
.reduce(function (p, t) {
if (p.indexOf(t) == -1)
p.push(t);
return p;
}, []);
// transform
var result = data.reduce(function (p, t) {
var monthName = formatMonthYear(t.dynamicHeaderColumn);
var existing = p.filter(function (t2) {
return t2.objectGroupingId == t.objectGroupingId;
});
if (existing.length) {
existing[0][monthName] = t.projectName;
} else {
var n = {
ObjectGroupingId: t.objectGroupingId
};
monthNames.forEach(function (m) {
n[m] = 0;
});
n[monthName] = t.projectName;
p.push(n);
}
return p;
}, []);
return result;
Is this possible? I'm not quite familiar in jQuery still learning it.
Why does the OP want to use jQuery? jQuery mainly targets the domain of querying and manipulating the DOM. The OP's task in contrast deals with a single pure data structure and how to traverse/transform it.
Thus the OP should stick to the OP's original approach which uses methods of built-in objects like Array and Object.
The next provided code example uses two nested reduce tasks in order to achieve exactly the OP's desired result ...
function collectGroupedProjectData({ lookup, result }, { ObjectGroupingId, ...restData }) {
let groupedData = lookup[ObjectGroupingId];
if (!groupedData) {
groupedData = lookup[ObjectGroupingId] = { ObjectGroupingId };
result.push(groupedData);
}
Object
.entries(restData)
.reduce((group, [key, value]) => {
if (value !== 0) {
Object.assign(group, { [key]: value });
}
return group;
}, groupedData);
return { lookup, result };
}
const data = [{
ObjectGroupingId: 1,
'Jan 2022': 'Project1',
'Feb 2022': 0,
'Mar 2022': 0,
'Apr 2022': 0,
'May 2022': 0,
}, {
ObjectGroupingId: 1,
'Jan 2022': 0,
'Feb 2022': 'Project2',
'Mar 2022': 0,
'Apr 2022': 0,
'May 2022': 0,
}, {
ObjectGroupingId: 1,
'Jan 2022': 0,
'Feb 2022': 0,
'Mar 2022': 'Project3',
'Apr 2022': 0,
'May 2022': 0,
}, {
ObjectGroupingId: 2,
'Jan 2022': 'Project4',
'Feb 2022': 0,
'Mar 2022': 0,
'Apr 2022': 0,
'May 2022': 0,
}, {
ObjectGroupingId: 2,
'Jan 2022': 0,
'Feb 2022': 'Project5',
'Mar 2022': 0,
'Apr 2022': 0,
'May 2022': 0,
}, {
ObjectGroupingId: 2,
'Jan 2022': 0,
'Feb 2022': 0,
'Mar 2022': 'Project6',
'Apr 2022': 0,
'May 2022': 0,
}];
const listOfGroupedProjects = data
.reduce(collectGroupedProjectData, { lookup: {}, result: [] }).result;
console.log({ listOfGroupedProjects });
.as-console-wrapper { min-height: 100%!important; top: 0; }
Here is a simple way to achieve this task using a dictionary. I highly recommend you read up on how a dictionary works - they are very useful!
Just a quick explanation of the code, in function(e,d) the i is the index and d is the value. The second - function(key, value) is a little different. The key will return the value before the : instead of an index. This is because you are iterating over the values of an object ({ and } define the start and end of the object). value will return anything to the right of the :.
Update: I have updated the code to allow for any number of ObjectGroups as well as a pure JavaScript version.
var input = [
{
"ObjectGroupingId": 1,
"Jan 2022": "Project1",
"Feb 2022": 0,
"Mar 2022": 0,
"Apr 2022": 0,
"May 2022": 0
},
{
"ObjectGroupingId": 1,
"Jan 2022": 0,
"Feb 2022": "Project2",
"Mar 2022": 0,
"Apr 2022": 0,
"May 2022": 0
},
{
"ObjectGroupingId": 1,
"Jan 2022": 0,
"Feb 2022": 0,
"Mar 2022": "Project3",
"Apr 2022": 0,
"May 2022": 0,
"Jan 1900": 0
},
{
"ObjectGroupingId": 2,
"Jan 2022": 0,
"Feb 2022": 0,
"Mar 2022": 0,
"Apr 2022": 0,
"May 2022": 0,
"Jan 1900": "Project10"
}
]
var currentObjectGrouping = null;
var outputListJQuery = [];
$.each(input, function(i,d){
$.each(d, function(key, value) {
if(key === 'ObjectGroupingId') currentObjectGrouping = value;
if(outputListJQuery[currentObjectGrouping] === undefined) outputListJQuery[currentObjectGrouping] = {};
if(value !== 0) {
outputListJQuery[currentObjectGrouping][key] = value;
}
});
});
outputListJQuery = outputListJQuery.filter(function( element ) {
return element !== undefined;
})
console.log("This is using JQuery!");
console.log(outputListJQuery);
//Pure JavaScript
var outputListJavascript = [];
input.forEach(e => {
for (const [key, value] of Object.entries(e)) {
if(key === 'ObjectGroupingId') currentObjectGrouping = value;
if(outputListJavascript[currentObjectGrouping] === undefined) outputListJavascript[currentObjectGrouping] = {};
if(value !== 0) {
outputListJavascript[currentObjectGrouping][key] = value;
}
}
});
outputListJavascript = outputListJavascript.filter(function( element ) {
return element !== undefined;
})
console.log("This is using only JavaScript!");
console.log(outputListJavascript);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
In a case where you have multiple ObjectGroupingIds the final result can be an object of objects where the high-level keys are the ObjectGroupingId values and the values the aggregated elements.
const data = [{"ObjectGroupingId": 1,"Jan 2022": "Project1","Feb 2022": 0,"Mar 2022": 0,"Apr 2022": 0,"May 2022": 0},{"ObjectGroupingId": 1,"Jan 2022": 0,"Feb 2022": "Project2","Mar 2022": 0,"Apr 2022": 0,"May 2022": 0},{"ObjectGroupingId": 1,"Jan 2022": 0,"Feb 2022": 0,"Mar 2022": "Project3","Apr 2022": 0,"May 2022": 0},{"ObjectGroupingId": 2,"Jan 2022": "Project4","Feb 2022": 0,"Mar 2022": 0,"Apr 2022": 0,"May 2022": 0},{"ObjectGroupingId": 2,"Jan 2022": 0,"Feb 2022": "Project5","Mar 2022": 0,"Apr 2022": 0,"May 2022": 0},{"ObjectGroupingId": 2,"Jan 2022": 0,"Feb 2022": 0,"Mar 2022": "Project6","Apr 2022": 0,"May 2022": 0}];
const aggData = data.reduce((acc, {ObjectGroupingId:id,...rest}) =>
({...acc,[id]:{...acc[id], ...Object.fromEntries(Object.entries(rest).filter(([k,v]) => v !== 0))}}), {}
);
console.log( aggData );