I am writing a tool for my non-tech users. They are not familiar with JSON so I build this tool so that they can easily interact with from the UI. The generated JSON will be used to processing the business from backend code.
The JSON array structure is as follow:
[
{
"groupCode": "Minimalism",
"dateRangeFrom": "01/09/2024",
"dateRangeTo": "30/09/2024",
"fulfillment": {
"slotNames": [
"10:00",
"20:00",
],
"slotIds": [1,2,3]
}
}
]
I got a problem when I want to optimize the array from many object like above. dateRangeFrom and dateRangeTo will initiate a range. I don’t want to have any range that overlaps each other. E.g:
If original JSON is as above, and via my UI I provide them, they added a new JSON object as follow:
{
"groupCode": "Minimalism",
"dateRangeFrom": "01/09/2024",
"dateRangeTo": "30/10/2024",
"fulfillment": {
"slotNames": [
"10:00",
"20:00",
"21:00"
],
"slotIds": [4]
}
}
I want the output as follow:
[
{
"groupCode": "Minimalism",
"dateRangeFrom": "01/09/2024",
"dateRangeTo": "30/09/2024",
"fulfillment": {
"slotNames": [
"10:00",
"20:00",
"21:00" //Added one from 2nd object
],
"slotIds": [1,2,3,4] //Added 4 from 2nd object
}
},
{
"groupCode": "Minimalism",
"dateRangeFrom": "01/10/2024",
"dateRangeTo": "30/10/2024",
"fulfillment": {
"slotNames": [
"10:00",
"20:00",
"21:00"
],
"slotIds": [4]
}
}
]
Any idea how can I do that?
The current code creates segments when data is removed from a date range. However, it doesn’t detect overlaps when a new object is added, so it can’t combine or extract data into segments properly. I need to ensure no overlapping date ranges to avoid issues when the client modifies data.
I want to add a function that checks the entire JSON array. If a newly added object has overlapping date ranges with existing ones, it should:
-
Add the “fulfillment” data from the new object to the existing one with the overlapping date range.
-
Remove the overlapping date range from the new object, keeping only the non-overlapping dates.
//Optimize & Organize JSON data
function optimizeJSONData(inputJSON) {
const { parse, isAfter, addDays, format } = dateFns;
//Remove empty slot Range (No Fulfillment SlotNames and SlotIds)
let filteredArray = inputJSON.filter(item =>
item.fulfillment.slotNames.length > 0 || item.fulfillment.slotIds.length > 0
);
// Remove Duplicated SlotNames & Duplicated SlotIds
filteredArray = filteredArray.map(item => ({
...item,
fulfillment: {
slotNames: [...new Set(item.fulfillment.slotNames)].sort((a, b) => {
const convertToMinutes = time => {
const [hours, minutes] = time.split(':').map(Number);
return hours * 60 + minutes;
};
return convertToMinutes(a) - convertToMinutes(b);
}),
slotIds: [...new Set(item.fulfillment.slotIds)].sort((a, b) => a - b)
}
}));
//Combine JSON
console.log("Removed Empty: ", filteredArray);
// Convert date strings to Date objects
const processedData = filteredArray.map(item => ({
...item,
dateRangeFrom: parse(item.dateRangeFrom, 'dd/MM/yyyy', new Date()),
dateRangeTo: parse(item.dateRangeTo, 'dd/MM/yyyy', new Date())
}));
// Group by 'groupCode' and 'fulfillment'
const grouped = _.groupBy(processedData, item => `${item.groupCode}-${JSON.stringify(item.fulfillment)}`);
const result = _.flatMap(grouped, (group) => {
const sortedGroup = _.sortBy(group, ['dateRangeFrom']);
// Create a shifted start date array
const shiftedStartDate = sortedGroup.map((d, i) => i === 0 ? null : addDays(sortedGroup[i - 1].dateRangeTo, 1));
// Calculate condition for merging intervals
const condition = sortedGroup.map((d, i) => i === 0 ? true : isAfter(d.dateRangeFrom, shiftedStartDate[i]) === false);
// Calculate cumulative sum (group ID)
const cumsum = condition.reduce((acc, val) => {
acc.push((acc[acc.length - 1] || 0) + (val ? 0 : 1));
return acc;
}, []);
// Add 'group' to the sortedGroup
const groupedByCumsum = _.groupBy(sortedGroup.map((d, i) => ({ ...d, group: cumsum[i] })), 'group');
// Aggregate start_date and end_date
return _.map(groupedByCumsum, (items) => ({
groupCode: items[0].groupCode,
fulfillment: items[0].fulfillment,
dateRangeFrom: format(_.minBy(items, 'dateRangeFrom').dateRangeFrom, 'dd/MM/yyyy'),
dateRangeTo: format(_.maxBy(items, 'dateRangeTo').dateRangeTo, 'dd/MM/yyyy')
}));
});
console.log("Optimized Result: ", result);
return result;
}