Restructure JavaScript object into array of objects with named keys - javascript

I have the following JavaScript object:
const groupedData = {
"1":[
{"x":"2021-02-05","y":1},
{"x":"2021-02-05","y":1},
{"x":"2021-02-06","y":1}
],
"2":[
{"x":"2021-02-05","y":1},
{"x":"2021-02-06","y":1},
{"x":"2021-02-07","y":1},
{"x":"2021-02-07","y":1},
{"x":"2021-02-07","y":1},
{"x":"2021-02-08","y":1}
]
}
I'd like to group the objects in each of the arrays by x and sum by y and shape the data into a slightly different structure so it can be consumed by a charting library. I have the following function which does the grouping and summing successfully:
const formattedData = [];
Object.keys(groupedData).map((key) => {
var totals = groupedData[key].reduce(function (r, o) {
(r[o.x]) ? r[o.x] += o.y : r[o.x] = o.y;
return r;
}, {});
formattedData.push({ 'name': key, 'data': totals })
});
This outputs the following:
[
{
"name":"1",
"data": {
"2021-02-05":2,
"2021-02-06":1
}
},
{
"name":"2",
"data": {
"2021-02-05":1,
"2021-02-06":1,
"2021-02-07":3,
"2021-02-08":1
}
}
]
However, I'd like the data to be in the following format:
[
{
"name":"1",
"data":[
{"x":"2021-02-05","y":2},
{"x":"2021-02-06","y":1}
]
},
{
"name":"2",
"data":[
{"x":"2021-02-05","y":1},
{"x":"2021-02-06","y":1},
{"x":"2021-02-07","y":3},
{"x":"2021-02-08","y":1}
]
}
]
I'm struggling to come up with a neat way to do this final part of the formatting. Could anyone advise how to modify the formatting function to implement this last step, in a neat way?
Many thanks

You could map the entries of the object and group the values.
const
data = { 1: [{ x: "2021-02-05", y: 1 }, { x: "2021-02-05", y: 1 }, { x: "2021-02-06", y: 1 }], 2: [{ x: "2021-02-05", y: 1 }, { x: "2021-02-06", y: 1 }, { x: "2021-02-07", y: 1 }, { x: "2021-02-07", y: 1 }, { x: "2021-02-07", y: 1 }, { x: "2021-02-08", y: 1 }] },
result = Object.entries(data).map(([name, data]) => ({
name,
data: Object.values(data.reduce((r, { x, y }) => {
r[x] ??= { x, y: 0 };
r[x].y += y;
return r;
}, {}))
}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

How can I create a dynamic array based on another array in javascript/typescript?

I want to create an array to loop over one of the parameters of the first array (in this example, the desired parameter is the DT) and check whether we have data for different applications on those dates. If we have it, it will put its value (in the second array) and if we don't have it, it will put 0.
What I did was also with const pluck = (arr, key) => arr.map(i => i[key]);, I obtained the desired field dates (but they had duplicate values). To remove duplicate values I used dates = [...new Set(dates)]; and finally looped over the final values and wrote a series of codes, but I didn't get what I wanted (Expected Array in below).
first_array = [
{
DT: "2022-01-01",
APP: "Application 1",
SPEED: 1547,
},
{
DT: "2022-01-01",
APP: "Application 2",
SPEED: 685,
},
{
DT: "2022-01-02",
APP: "Application 1",
SPEED: 500,
},
{
DT: "2022-01-02",
APP: "Application 2",
SPEED: 300,
},
{
DT: "2022-01-02",
APP: "Application 3",
SPEED: 600,
},
{
DT: "2022-01-03",
APP: "Application 1",
SPEED: 1000,
},
]
Expected Array:
desire_array = [
{
Name: "Application1",
Values: [1547, 500, 1000],
ValuesWithDate: [{x: '2022-01-01', y: 1547}, {x: '2022-01-02', y: 500}, {x: '2022-01-03', y: 1000}],
},
{
Name: "Application2",
Values: [685, 300, 0],
ValuesWithDate: [{x: '2022-01-01', y: 685}, {x: '2022-01-02', y: 300}, {x: '2022-01-03', y: 0}],
},
{
Name: "Application3",
Values: [0, 600, 0],
ValuesWithDate: [{x: '2022-01-01', y: 0}, {x: '2022-01-02', y: 600}, {x: '2022-01-03', y: 0}],
},
]
The reason I need to do this is to create a series that I can use to display the chart with ApexCharts.
Real data can also be displayed from this api as JSON.
You can do:
const first = [{DT: '2022-01-01',APP: 'Application 1',SPEED: 1547,},{DT: '2022-01-01',APP: 'Application 2',SPEED: 685,},{DT: '2022-01-02',APP: 'Application 1',SPEED: 500,},{DT: '2022-01-02',APP: 'Application 2',SPEED: 300,},{DT: '2022-01-02',APP: 'Application 3',SPEED: 600,},{DT: '2022-01-03',APP: 'Application 1',SPEED: 1000,}]
const dates = [...new Set(first.map(({ DT }) => DT))]
const apps = [...new Set(first.map(({ APP }) => APP))]
const result = apps.reduce((acc, app) => {
const appData = Object.assign(
{},
{
Name: app.replace(/ /, ''),
Values: [],
ValuesWithDate: [],
}
)
dates.forEach((date) => {
const data = first.find(({ DT, APP }) => DT === date && APP === app)
appData.ValuesWithDate.push({ x: date, y: data ? data.SPEED : 0 })
appData.Values.push(data ? data.SPEED : 0)
})
acc.push(appData)
return acc
}, [])
console.log(result)
You can try with something like this :
const convertArray = (arr) => arr.reduce((prev, current) => {
const existingIndex = prev.findIndex((p) => p.Name === current.APP);
if (existingIndex > -1) {
const currentApp = prev[existingIndex];
currentApp.Values.push(current.SPEED);
currentApp.ValuesWithDate.push({x: current.DT, y: current.SPEED});
prev[existingIndex] = currentApp;
} else {
prev.push({Name: current.APP, Values: [current.SPEED], ValuesWithDate:[{x: current.DT, y: current.SPEED}]})
}
return prev;
}, []);
And use it like this :
const desire_array = convertArray(first_array)
const convert = (dates, data) => {
return Object.values(data.reduce((acc, curr) => {
if (!acc[curr.APP]) {
acc[curr.APP] = {
name: curr.APP,
valuesWithDate: []
};
}
acc[curr.APP].valuesWithDate.push({
x: curr.DT,
y: curr.SPEED
});
return acc;
}, {})).map((dataWithoutGaps) => {
const valuesWithDate = [...new Set(dates)].map(date => {
const el = dataWithoutGaps.valuesWithDate.find(e => e.x === date);
return {
x: date,
y: el ? el.y : 0
};
});
return {
ValuesWithDate: valuesWithDate,
Values: valuesWithDate.map(e => e.y),
Name: dataWithoutGaps.name
}
});
};
console.log(convert(first_array.map(e => e.DT), first_array));
Expected:
[{"ValuesWithDate":[{"x":"2022-01-01","y":1547},{"x":"2022-01-02","y":500},{"x":"2022-01-03","y":1000}],"Values":[1547,500,1000],"Name":"Application 1"},{"ValuesWithDate":[{"x":"2022-01-01","y":685},{"x":"2022-01-02","y":300},{"x":"2022-01-03","y":0}],"Values":[685,300,0],"Name":"Application 2"},{"ValuesWithDate":[{"x":"2022-01-01","y":0},{"x":"2022-01-02","y":600},{"x":"2022-01-03","y":0}],"Values":[0,600,0],"Name":"Application 3"}]
Your expected result can be achieved by this code.
let filtered_app = new Set();
const obj = [];
first_array.forEach(item=>{
filtered_app.add(item.APP);
});
filtered_app.forEach(app =>{
first_array.forEach(item =>{
if(item.APP == app){
const exists = obj.findIndex(elem => elem.Name == app);
if(exists != '-1'){
obj[exists].Values.push(item.SPEED);
obj[exists].ValuesWithDate.push({x: item.DT, y: item.SPEED});
}
else{
obj.push({Name: app, Values: [item.SPEED], ValuesWithDate: [{x: item.DT, y: item.SPEED}]});
}
}
});
});
console.log(obj);
Hope it helps.

Return a random Object containing a specific parameter from an object array

I'm currently storing data as objects inside a array in the following way:
let data = [];
module.exports.init = function() {
database.pool.query("SELECT * FROM data", (error, rows) => {
if (error) {
logUtil.log.error(`Loading failed: ${ error.message }`);
}
else {
rows.forEach((row) => data.push({dimension: row.dimension, x: row.x, y: row.y, z: row.z}));
logUtil.log.info(data);
}
});
};
data will hold the following now: [{ dimension: 2, x: -973.097, y: -133.411, z: 38.2531 }, { dimension: 3, x: -116.746, y: -48.414, z: 17.226 }, { dimension: 2, x: -946.746, y: -128.411, z: 37.786 }, { dimension: 2, x: -814.093, y: -106.724, z: 37.589 }]
Now I'm trying to receive a random object from this array storing a specific dimension parameter.
For example I want to return a random object storing the dimension: 2
I've tried to filter the array using something like:
let result = jsObjects.filter(data => {
return data.dimension === 2
})
then return a random object from the result.
Question: How could I receive this random object in the best way?
You can do it in two steps.
Get all record which satisfy criteria like dimension === 2
let resultArr = jsObjects.filter(data => {
return data.dimension === 2
})
Get random object from result.
var randomElement = resultArr[Math.floor(Math.random() * resultArr.length)];
var arr = [{ dimension: 2, x: -973.097, y: -133.411, z: 38.2531 }, { dimension: 3, x: -116.746, y: -48.414, z: 17.226 }, { dimension: 2, x: -946.746, y: -128.411, z: 37.786 }, { dimension: 2, x: -814.093, y: -106.724, z: 37.589 }]
//Filter out with specific criteria
let resultArr = arr.filter(data => {
return data.dimension === 2
})
//Get random element
var randomElement = resultArr[Math.floor(Math.random() * resultArr.length)];
console.log(randomElement)
You could use Math.random() and in the range of 0 to length of array.
let result = jsObjects.filter(data => {
return data.dimension === 2
})
let randomObj = result[Math.floor(Math.random() * result.length)]

Distinct Dates with its highest value from an object with javascript

I have something like this:
data = [
{
DateMeasured:"2018-08-27T04:46:25",
Steps:100
},
{
DateMeasured:"2018-08-27T04:46:25",
Steps:500
},
{
DateMeasured:"2018-08-27T04:46:25",
Steps:800
},
{
DateMeasured:"2018-08-26T04:46:25",
Steps:400
},
{
DateMeasured:"2018-08-26T04:46:25",
Steps:300
},
{
DateMeasured:"2018-08-25T04:46:25",
Steps:100
}
];
I have an object of data like above, now I want to recreate object with discrict dates but its highest steps, but now i want like this:
data = [
{
DateMeasured:"2018-08-27T04:46:25",
Steps:800
},
{
DateMeasured:"2018-08-26T04:46:25",
Steps:400
},
{
DateMeasured:"2018-08-25T04:46:25",
Steps:100
}
];
How can I achieve this goal?
You could reduce the array by checking the last inserted object with the same date and if not found, insert the object, otherwise check the value and update the array with a greater Step property.
var data = [{ DateMeasured: "2018-08-27T04:46:25", Steps: 100 }, { DateMeasured: "2018-08-27T04:46:25", Steps: 500 }, { DateMeasured: "2018-08-27T04:46:25", Steps: 800 }, { DateMeasured: "2018-08-26T04:46:25", Steps: 400 }, { DateMeasured: "2018-08-26T04:46:25", Steps: 300 }, { DateMeasured: "2018-08-25T04:46:25", Steps: 100 }],
result = data.reduce((r, o) => {
var index = r.findIndex(({ DateMeasured }) => DateMeasured === o.DateMeasured);
if (index === -1) {
r.push(o);
return r;
}
if (r[index].Steps < o.Steps) {
r[index] = o;
}
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
If you need to sort by the number of steps and take the first 3 elements:
const data = [ { DateMeasured:"2018-08-27T04:46:25", Steps:100 }, { DateMeasured:"2018-08-27T04:46:25", Steps:500 }, { DateMeasured:"2018-08-27T04:46:25", Steps:800 }, { DateMeasured:"2018-08-26T04:46:25", Steps:400 }, { DateMeasured:"2018-08-26T04:46:25", Steps:300 }, { DateMeasured:"2018-08-25T04:46:25", Steps:100 } ];
const sorted = data.sort((a, b) => b.Steps - a.Steps)
const takeFirst3 = sorted.slice(0, 3)
console.log(takeFirst3)

Split array into chunk of different size on the basis of their attribute

I have this following array
var array=[{ semster:1, name:Book1 }, { semster:1, name:Book2 }, { semster:2, name:Book4 }, { semster:3, name:Book5 }, { semster:3, name:Book6 }, { semster:4, name:Book7 }]
Now I want to sort my array to split the current array into chunks of array like following
var array=[[{ semster:1, name:Book1 }, { semster:1, name:Book2 }],[ { semster:2, name:Book4 }], [{ semster:3, name:Book5 }, { semster:3, name:Book6 }], [{ semster:4, name:Book7 }]]
I have tried to achieve this with following code :
function splitIntoSubArray(arr, count) {
var newArray = [];
while (arr.length > 0) {
newArray.push(arr.splice(0, count));
}
return newArray;
}
But this can only divide the array on the basis of fixed size. Any kind of suggestion is appreciated.
Thanks
You can simply use Array.reduce() to group items by semester. Object.values() on the map gives you the desired result.
var array=[{ semster:1, name:"Book1" }, { semster:1, name:"Book2" }, { semster:2, name:"Book4" }, { semster:3, name:"Book5" }, { semster:3, name:"Book6" }, { semster:4, name:"Book7" }];
var result = Object.values(array.reduce((a, curr)=>{
(a[curr.semster] = a[curr.semster] || []).push(curr);
return a;
},{}));
console.log(result);
You could reduce the array by checking the last group with the same semester.
var array = [{ semester: 1, name: 'Book1' }, { semester: 1, name: 'Book2' }, { semester: 2, name: 'Book4' }, { semester: 3, name: 'Book5' }, { semester: 3, name: 'Book6' }, { semester: 4, name: 'Book7' }],
grouped = array.reduce((r, o) => {
var last = r[r.length - 1];
if (last && last[0].semester === o.semester) {
last.push(o);
} else {
r.push([o]);
}
return r;
}, []);
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }

How to update an array of objects according another array of objects

I have two arrays:
initial=[{code:"1",size: 0},{code:"2",size: 0},{code:"3",size: 0},{code:"4",size: 0}];
update=[{code:"1",size: 100},{code:"2",size: 100},{code:"2",size: 120}];
I need to get new array like this:
I want to group by code field and do the sum of size after grouping.
new=[{code:"1",size: 100},{code:"2",size: 220},{code:"3",size: 0},{code:"4",size: 0}];
You could use a hash table for the objects with same code.
var initial = [{ code: "1", size: 0 }, { code: "2", size: 0 }, { code: "3", size: 0 }, { code: "4", size: 0 }],
update = [{ code: "1", size: 100 }, { code: "2", size: 100 }, { code: "2", size: 120 }],
hash = Object.create(null),
result = initial.map(function (o) {
return hash[o.code] = { code: o.code, size: 0 };
});
update.forEach(function (o) {
hash[o.code].size += o.size;
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Check this
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
var o1 = { a: 1, b: 1, c: 1 };
var o2 = { b: 2, c: 2 };
var o3 = { c: 3 };
var obj = Object.assign({}, o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
You can use .map(), .filter() and .reduce() methods of Array.prototype
let initial = [{code:"1",size: 0},{code:"2",size: 0},{code:"3",size: 0},{code:"4",size: 0}];
let update = [{code:"1",size: 100},{code:"2",size: 100},{code:"2",size: 120}];
let res = initial.map(({code, size}, index) =>
({code, size: size
// get elements having same `"code"` property value from `update` array
// add the result
+ update.filter(({code:c}) => code === c)
.reduce((n, {size:s}) => n+= s, 0)})
);
console.log(res);
Here is a version using an ES6 Map in a functional programming style:
function addObjects(acc, add) {
return Array.from(
add.reduce((mp, {code, size}) => mp.set(code, mp.get(code) + size),
new Map(acc.map(({code, size}) => [code, size]))),
([code, size]) => ({code, size})
);
}
const initial = [{ code: "1", size: 0 }, { code: "2", size: 0 }, { code: "3", size: 0 }, { code: "4", size: 0 }],
update = [{ code: "1", size: 100 }, { code: "2", size: 100 }, { code: "2", size: 120 }];
const res = addObjects(initial, update);
console.log(res);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can use array#concat to join your array and then use array#reduce to sum up the sizes inside an object and then get the values from that object.
const initial=[{code:"1",size: 0},{code:"2",size: 0},{code:"3",size: 0},{code:"4",size: 0}],
update=[{code:"1",size: 100},{code:"2",size: 100},{code:"2",size: 120}];
let result = initial
.concat(update)
.reduce((res, {code, size}) => {
res[code] = {code, size : (res[code]|| {size:0}).size + size};
return res;
},Object.create(null));
let output = Object.values(result);
console.log(output);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories