How to sum properties of elements in an object-array? - javascript

I have this array, and I want to sum property (y) of the array elements when x matches certain criteria. For example, if "x" has the same string value between "/" and "?" as another object then add their "y" property together.
const data = [
{
"x": "/shop.html",
"y": 3
},
{
"x": "/",
"y": 2
},
{
"x": "/?test324",
"y": 1
},
{
"x": "/account.html",
"y": 1
},
{
"x": "/account.html?test1",
"y": 1
},
{
"x": "/shop.html?test543",
"y": 1
}
]
And it should be like this at the end
const expectedResult = [
{
"x": "/shop.html",
"y": 4
},
{
"x": "/",
"y": 3
},
{
"x": "/account.html",
"y": 2
},
]
So as you can see the 2nd array doesn't have the "?xxx" thing, they are all "merged" based on string value between last "/" and "?"
Tried to do something like this
let output = res.data.data.reduce(function (accumulator, cur) {
let x = cur.x,
found = accumulator.find(function (elem) {
elem.x = elem.x.split("?")[0];
return elem.x == x;
});
if (found) found.y += cur.y;
else accumulator.push(cur);
return accumulator;
}, []);
But duplicated values doesn't add themselves.
It returns me this
[
{
"x": "/shop.html",
"y": 3
},
{
"x": "/",
"y": 2
},
{
"x": "/",
"y": 1
},
{
"x": "/account.html",
"y": 1
},
{
"x": "/account.html",
"y": 1
},
{
"x": "/shop.html?test543",
"y": 1
}
]
Any idea?

The below may be one possible solution to achieve the desired objective.
Code Snippet
// a small helper method to convert key by leaving out the z-s in: '/xxxx?zzz'
const convertKey = x => (x.split('?')[0]);
// use reduce to iterate thru the array & obtain a result-object
// destructure to get 'x', 'y'
// if 'x' already present, add 'y'
// else create an object with 'x', 'y' props
// return the `Object.values` of the result-object
const transform = arr => (
Object.values(
arr.reduce(
(acc, {x, y}) => ({
...acc,
[convertKey(x)]: {
...(acc[convertKey(x)] || {x}),
y: (acc[convertKey(x)]?.y || 0) + y
}
}),
{}
)
)
);
const data = [
{
"x": "/shop.html",
"y": 3
},
{
"x": "/",
"y": 2
},
{
"x": "/?test324",
"y": 1
},
{
"x": "/account.html",
"y": 1
},
{
"x": "/account.html?test1",
"y": 1
},
{
"x": "/shop.html?test543",
"y": 1
}
];
console.log(transform(data));
Explanation
The above code-snippet has inline comments describing the steps. For further description, please post specific questions on comments below, if required.

Related

Get all dates from first date of week in Array of objects in JavaScript

Need help on solving this, should be a small task, but could not figured it out.
Problem: data is an array of objects with the first date of the week in x and a value for that week in y.
need a solution/function that will return all dates in that week in x and the same value in y for each date in that particular week.
I'm using moment.js in case needed.
Input:
data =
[
{
"x": "2022-07-25",
"y": 0.5
},
{
"x": "2022-08-01",
"y": 0.2
},
{
"x": "2022-08-08",
"y": 0.3
}
]
Expected output:
data =
[
{
"x": "2022-07-25",
"y": 0.5
},
{
"x": "2022-07-26",
"y": 0.5
},
{
"x": "2022-07-27",
"y": 0.5
},
{
"x": "2022-07-28",
"y": 0.5
},
{
"x": "2022-07-29",
"y": 0.5
},
{
"x": "2022-07-30",
"y": 0.5
},
{
"x": "2022-07-31",
"y": 0.5
},
{
"x": "2022-08-01",
"y": 0.2
},
{
"x": "2022-08-02",
"y": 0.2
},
{
"x": "2022-08-03",
"y": 0.2
},
{
"x": "2022-08-04",
"y": 0.2
},
{
"x": "2022-08-05",
"y": 0.2
},
{
"x": "2022-08-06",
"y": 0.2
},
{
"x": "2022-08-07",
"y": 0.2
},
{
"x": "2022-08-08",
"y": 0.3
},{
"x": "2022-08-09",
"y": 0.3
}
,{
"x": "2022-08-10",
"y": 0.3
}
,{
"x": "2022-08-11",
"y": 0.3
},{
"x": "2022-08-12",
"y": 0.3
},{
"x": "2022-08-13",
"y": 0.3
},{
"x": "2022-08-14",
"y": 0.3
}
]
You could do this by using flatMap and an extension for addDays found in this answer
const data = [{
"x": "2022-07-25",
"y": 0.5
},
{
"x": "2022-08-01",
"y": 0.2
},
{
"x": "2022-08-08",
"y": 0.3
}
]
Date.prototype.addDays = function(days) {
var date = new Date(this.valueOf());
date.setDate(date.getDate() + days);
return date;
}
const result = data.flatMap(item => {
const date = new Date(item.x)
return [0,1,2,3,4,5,6,7].map(d => (
{
x: date.addDays(d).toISOString(),
y: item.y
}
));
})
console.log(result)
You can try with reduce
const data = [{
"x": "2022-07-25",
"y": 0.5
},
{
"x": "2022-08-01",
"y": 0.2
},
{
"x": "2022-08-08",
"y": 0.3
}
]
const finalResult = data.reduce((result, current) => {
const {
x,
y
} = current
//convert the string date to a date type
const currentDate = new Date(x)
//add the current date to the result by default
result.push(current)
const days = 6
for (let day = 1; day <= days; day++) {
//change the current date to the next date
currentDate.setDate(currentDate.getDate() + 1)
//add the new date data to the result
result.push({
x: currentDate.toISOString().substring(0, 10),
y: y
})
}
return result
}, [])
console.log(finalResult)
Try this!
const expandEveryWeek = function(data){
//Array for result
const result = [];
//Iterate all array parameters (Assuming first element is the first of week)
//It's always Monday
for(const obj of data){
const {x,y} = obj; //Extract values (x,y)
const date = new Date(x); //Create date with x
for(let j=0;j<7;j++){
result.push({ x : date.toISOString().substring(0,10), y}); //Insert first (0 index)
date.setDate(date.getDate() + 1) //Add day
}
}
return result;
}
Ok so, pass data object to above function.
The assumption is that each element of this array contains a date that always indicates the Monday of the week to expand
Iterate all obj inside data and for every one extract <date,value>. For every date, iterate other 7 step (one for the start date and other 6 for following days and increment date of one single day, after this, push it inside array with his value y.

Transform inner object as property of the outher object

I have an object like this in my NodeJS backend:
{
"x": "2021",
"y": {
"Kumada, K": 2,
"Wu, XY": 4,
"da Silva, BJP": 2
}
}
And i would need it to become like this:
{
"x": "2021",
"Kumada, K": 2
"Wu, XY": 4,
"da Silva, BJP": 2
}
EDIT: The "y" is dynamic which means it can have any number of different names, for example:
{
"x": "2021",
"y": {
"Josh, K": 2,
"Bob": 4,
"Joseph": 2,
"John": 0
}
}
Any tips? Appreciate any help!
Below approach first creates an object based on a.y and then add all properties from a one by one using Object.keys(). Then it removes the y key, since it is not required.
let a = {
"w" : "Hello",
"x": "2021",
"y": {
"Kumada, K": 2,
"Wu, XY": 4,
"da Silva, BJP": 2
}
}
let b = a.y;
Object.keys(a).forEach((x)=>{
b[x] = a[x];
})
delete b.y
This will be a shallow copy
For eg : If you have a["x"] as { 'prop1' : 22, 'prop2' : 33 }. And you modify a["x"]["prop1"] = 22; This will be reflected in your final object too.
Get to know spread syntax. It's magic sauce for this kind of thing
let o = {
"x": "2021", "y": { "Kumada, K": 2, "Wu, XY": 4, "da Silva, BJP": 2 }
}
let newobj = { ...o, ...o.y };
delete newobj.y;
console.log(newobj)

Sorting a JSON response in Angular5 using typescript

I am making an API call from Angular 5, the response is coming in below format.
let ob = { "metadata":{
"lastSyncTime":"2000-11-21T16:07:53",
"dataFromDB":true
},
"allocationReports":[
{
"allocatedUserCount":130,
"healthGoalName":"Feel Happier"
},
{
"allocatedUserCount":150,
"healthGoalName":"Quit Smoking"
},
{
"allocatedUserCount":100,
"healthGoalName":"Eat Healthier"
}
],
"overall":{
"usersWithGoalCount":0,
"registeredCount":500,
"eligibleCount":280
}
}
I need to transform this data into a list of lists(or Array of Arrays) so that I can plot multiple donut charts on this data. I have tried with multiple methods like using .map, but getting all the values in single list. How can I build the data in below format.
The required format is : [Array should be sorted.]
[
[
{ "x": "Eat Healthier", "y": 100 },
{ "x": "Total registered", "y": 500 }
],
[
{ "x": "Feel Happier", "y": 130 },
{ "x": "Total registered", "y": 500 }
],
[
{ "x": "Quit Smoking", "y": 150 },
{ "x": "Total registered", "y": 500 }
]
]
I have written below code.
r=ob.allocationReports.map(o=>{return [{x:o.healthGoalName,y:o.allocatedUserCount},{x: "Total registered", y: ob.overall.registeredCount }]})
But the result I am getting is not sorted. How can I sort this.
Try this it will sort them in ascending order
ob={"metadata":{ "lastSyncTime":"2000-11-21T16:07:53", "dataFromDB":true }, "allocationReports":[ { "allocatedUserCount":130, "healthGoalName":"Feel Happier" }, { "allocatedUserCount":100, "healthGoalName":"Eat Healthier" },{ "allocatedUserCount":150, "healthGoalName":"Quit Smoking" } ], "overall":{ "usersWithGoalCount":0, "registeredCount":500, "eligibleCount":280 } }
r=ob.allocationReports.map(o=>{return [{x:o.healthGoalName,y:o.allocatedUserCount},{x: "Total registered", y: ob.overall.registeredCount }]}).sort((a,b)=>a[0].y-b[0].y)
console.log(r)

How to get id from main Entity and wrap it into nested one in normalizr

Hi I have a problem with normalization of my wrapped object.
I have a data array of dashboards and layouts object which contains breakpoints for responsive views.
Want to normalize them all into Two Entities, I mean Dashboards and Layouts.
{
"dashboards":[
{
"id":1,
"name":"First",
"mode":"2",
"layouts":{
"lg":[
{
"x":0,
"y":0,
"w":2,
"h":2,
"i":"sm1"
},
{
"x":2,
"y":0,
"w":2,
"h":2,
"i":"sm2"
}
],
"md":[
{
"x":2,
"y":0,
"w":2,
"h":2,
"i":"sm2"
}
]
}
}
]
}
I tried to do it like that. But I cant get the key from Dashboards and put it to the Layouts, beacuse it's one to one relationship.
const layouts = new schema.Entity('layouts');
const mode = new schema.Entity('modes');
const dashboards = new schema.Entity('dashboards', {
layouts: layouts,
mode: mode
});
const dashboardListSchema = new schema.Array(dashboards);
const normalizedData = normalize(response, dashboardListSchema);
My output is like dat for now:
Dashboards: { "1": { "id": 1, "name": "Główny", "mode": "2" } }
Layouts: { "undefined": { "lg": [ { "x": 0, "y": 0, "w": 2, "h": 2, "i": "sm1" }, { "x": 2, "y": 0, "w": 2, "h": 2, "i": "sm2" } ], "md": [ { "x": 2, "y": 0, "w": 2, "h": 2, "i": "sm2" } ] } }
I want id of dashboard instead of undefined. Could anyone help me?
This is my solution :)
const layouts = new schema.Entity('layouts', {}, {
idAttribute: (value, parent) => parent.id
});

Displaying a single series multi bar chart using nvd3 library

Does anyone know how I would go making a multi bar graph to be single series? In a working example that i've seen of how i want my graph to look, this function was being used for the data.
function dataFactory(seriesNum, perSeries) {
return new d3.range(0,seriesNum).map(function(d,i) { return {
key: 'Stream ' + i,
values: new d3.range(0,perSeries).map( function(f,j) {
return {
y: 10 + Math.random()*100,
x: j
}
})
};
});
}
Below is the code i'm currently using and I will also upload a picture so you can see that my labels are off position because it isn't single series.
function loadBar(){
$.getJSON('data5.json', function (json) {
var data1 = [];
for (var key in json) {
if (json.hasOwnProperty(key)) {
var item = json[key];
data1.push({
key: item.key,
values: item.values
});
}
}
var chart;
nv.addGraph(function() {
chart = nv.models.multiBarChart()
.color(d3.scale.category10().range())
.margin({bottom: 100})
.transitionDuration(300)
.delay(0)
//.rotateLabels(45)
;
chart.multibar
.hideable(true);
chart.reduceXTicks(false).staggerLabels(true).groupSpacing(0.2);
chart.xAxis
.axisLabel("Players")
.showMaxMin(false);
chart.yAxis
.axisLabel('Hours Played')
.tickFormat(d3.format('d'));
d3.select('#chart1 svg')
.datum(data1)
.call(chart);
nv.utils.windowResize(chart.update);
chart.dispatch.on('stateChange', function(e) { nv.log('New State:', JSON.stringify(e)); });
return chart;
});
});
}
$(document).ready(function(){
loadBar();
});
data5.json(just in case someone needs to see it)
{
"Member1": {
"key":"test10",
"values": [
{
"x": "test10",
"y": 20
}
]
},
"Member2":{
"key":"test9",
"values": [
{
"x": "test9",
"y": 10
}
]
},
"Member3":{
"key":"test8",
"values": [
{
"x": "test8",
"y": 4
}
]
},
"Member4":{
"key":"test7",
"values": [
{
"x": "test7",
"y": 12
}
]
},
"Member5":{
"key":"test6",
"values": [
{
"x": "test6",
"y": 30
}
]
},
"Member6":{
"key":"test5",
"values": [
{
"x": "test5",
"y": 8
}
]
}
,
"Member7":{
"key":"test4",
"values": [
{
"x": "test4",
"y": 27
}
]
},
"Member8":{
"key":"test3",
"values": [
{
"x": "test3",
"y": 17
}
]
},
"Member9":{
"key":"test2",
"values": [
{
"x": "test2",
"y": 2
}
]
},
"Member10":{
"key":"test1",
"values": [
{
"x": "test1",
"y": 55
}
]
}
![enter image description here][2]}
The expected data format for the multi-bar chart is an array of object, each of which represent a data series. Within each series object, there should be a key property naming that series, and a values array with the data points. The values array should have an object for each bar, with a categorical x value and a numerical y value.
For example, if I "stringify" the results of their data-generating function (after reducing the parameters so I only get two data series with five bars each), it looks like this:
[{
"key": "Stream0",
"values": [{
"x": 0,
"y": 0.16284738584101344
}, {
"x": 1,
"y": 2.370283172738109
}, {
"x": 2,
"y": 0.1631208266452718
}, {
"x": 3,
"y": 0.24609871793543797
}, {
"x": 4,
"y": 1.5096133160633776
}]
}, {
"key": "Stream1",
"values": [{
"x": 0,
"y": 0.12566330679904006
}, {
"x": 1,
"y": 0.1321859413211272
}, {
"x": 2,
"y": 1.4798247902549135
}, {
"x": 3,
"y": 0.10870538273358979
}, {
"x": 4,
"y": 0.16155091711225184
}]
}]
The graph looks like this:
Each series is graphed in a different colour. The bars are grouped according to their x value, side-by-side or you can switch to stacked.
The reason you were getting one narrow bar for each of your categories is because you have 11 different data series, each with one bar that has a different x-value. So for each x-value, the graph leaves room for all the data series to be plotted side-by-side, even though it doesn't have data for them.
You either need to group all your bars into one data series, with the test identified via the x-value, or you need to give them all the same x-value, with the test identified via the series key.
I know you've already got the first option pretty much working, based your other question on the discrete bar chart function.
The easiest way to modify this code to see what it looks like the other way (11 series, each with only one bar), is to tell the chart function to just use a constant value for x:
chart.x(function(d){return "test";})
With that, and data similar to yours (many series, each with only one data point), you get a chart that switches from a bar chart to a stacked area chart, like this:
(P.S., You'll of course want to remove the number-formatting tickFormat function so that you don't get "NaN" like in these pictures!)

Categories

Resources