Transform inner object as property of the outher object - javascript

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)

Related

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

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.

Convert string value to variable reference in javascript

I have the following code where I'm attempting to reference the values of one JSON object with the variables of another:
const ch = {
"columns": {
"COL1": {
"position": 1,
"composites": ["VAR1", "VAR3"]
},
"COL2": {
"position": 3,
"composites": ["VAR2"]
},
"COL3": {
"position": 2,
"composites": ["VAR4"]
}
}
}
const dataset = [{
"VAR1": "alpha",
"VAR2": 2,
"VAR3": "1015",
"VAR4": "z",
},
{
"VAR1": "beta",
"VAR2": 701,
"VAR3": "1023",
"VAR4": "z"
}
]
for (let l = 0; l < dataset.length; l++) {
for (const {
position,
composites
} of Object.values(ch.columns).sort((a, b) => a.position - b.position)) {
console.log(position, composites[0], dataset[l].VAR1)
/* eval[dataset[l].composites[0]], this[dataset[l].composites[0]]*/
}
}
The program correctly orders the columns and I can refer both values from 'ch', but I would like to use the first composites value as a variable reference to the dataset. Having googled the question I followed a couple of recommendations to either use 'this' or 'eval', but neither work. Where am I going wrong?
Ideally, if I could get the commented out code working the log should look like the following:
1 VAR1 alpha alpha
2 VAR4 alpha z
3 VAR2 alpha 2
1 VAR1 beta beta
2 VAR4 beta z
3 VAR2 beta 701
Use dataset[l][composites[0]] to get the additional column. See Dynamically access object property using variable
const ch = {
"columns": {
"COL1": {
"position": 1,
"composites": ["VAR1", "VAR3"]
},
"COL2": {
"position": 3,
"composites": ["VAR2"]
},
"COL3": {
"position": 2,
"composites": ["VAR4"]
}
}
}
const dataset = [{
"VAR1": "alpha",
"VAR2": 2,
"VAR3": "1015",
"VAR4": "z",
},
{
"VAR1": "beta",
"VAR2": 701,
"VAR3": "1023",
"VAR4": "z"
}
]
for (let l = 0; l < dataset.length; l++) {
for (const {
position,
composites
} of Object.values(ch.columns).sort((a, b) => a.position - b.position)) {
console.log(position, composites[0], dataset[l].VAR1, dataset[l][composites[0]])
}
}

Sort by the sum of array in object

I am looking for a solution to sort an array by the sum of an array property within an object.
For example if the main array is
[
{
"Grid": {
"Day": [
11,
12
]
},
"Name": "One"
},
{
"Grid": {
"Day": [
5,
2
]
},
"Name": "Two"
}
]
How can I sort the sum of Day to return as
[
{
"Grid": {
"Day": [
5,
2
]
},
"Name": "Two"
},
{
"Grid": {
"Day": [
11,
12
]
},
"Name": "One"
}
]
You just need sort your array with comparator, that uses reduce to calc sum of inner array values:
let arr = [{"Grid": {"Day": [11,12]}, "Name": "One"},
{"Grid": {"Day": [5,2]}, "Name": "Two"},
{"Grid": {"Day": [1,2]}, "Name": "Two"}];
let sum = el => el.Grid.Day.reduce((a,b) => a + b);
arr.sort((a,b) => sum(a) - sum(b));
console.log(arr)
You can use a combination of reduce to sum the array, and sort to order the output:
var input = [
{
"Grid": {
"Day": [
11,
12
]
},
"Name": "One"
},
{
"Grid": {
"Day": [
5,
2
]
},
"Name": "Two"
}
];
var result = input.sort( (a,b) => sumOfDay(a) - sumOfDay(b));
console.log(result);
function sumOfDay(obj){
return obj.Grid.Day.reduce( (acc,curr) => acc + curr, 0);
}
Note that Array.prototype.sort actually mutates the original array in place. so the above could also do
input.sort( (a,b) => sumOfDay(a) - sumOfDay(b));
console.log(input);
So, don't fall into the trap of thinking the original array is unchanged just because I assigned the result to result!.
If you do wish to sort a copy of the array do this:
var result = input.slice().sort( (a,b) => sumOfDay(a) - sumOfDay(b));
Create a new Array of a by mapping through it and using reduce on the Day Array of Grid to get your sum which you can compare within a sort to return your list sorted by summed days.
const a = [
{
"Grid": {
"Day": [
11,
12
]
},
"Name": "One"
},
{
"Grid": {
"Day": [
5,
2
]
},
"Name": "Two"
}
]
const daySum = ({Grid}) => Grid.Day.reduce((prev, curr) => prev+curr, 0)
const sorted = [...a].sort(daySum)
console.log(sorted)
console.log(a) //Original array intact
Just "another" approach to solve the issue: assuming you (someday, later, eventually) may need to sort again, a good approach may also be to add a property to each grid item holding the sum of the days, avoiding the .reduce call every time you need to sort the array.
In this approach, .forEach is used to create the new property (through .reduce), and then .sort is used to sort the array in-place.
const input = [
{
"Grid": {
"Day": [
11,
12
]
},
"Name": "One"
},
{
"Grid": {
"Day": [
5,
2
]
},
"Name": "Two"
}
];
// Add a DaySum property evaluating the sum of the days.
input.forEach(i => i.Grid.DaySum = i.Grid.Day.reduce((a,b) => a + b));
// ^--- the second parameter (initial value) is unneeded here due to the fact that all elements are actually numeric, hence if the initial value is the first element of the array, which is a number already.
// Sor the array by that property.
input.sort((a,b) => a.Grid.DaySum - b.Grid.DaySum);
console.log(input);
Or, as suggested by #Andreas below, you can directly assign the property while sorting:
const input = [
{
"Grid": {
"Day": [
11,
12
]
},
"Name": "One"
},
{
"Grid": {
"Day": [
5,
2
]
},
"Name": "Two"
}
];
const sum = (a,b) => a + b;
input.sort((a,b) => {
a.Grid.DaySum = a.Grid.DaySum || a.Grid.Day.reduce(sum);
b.Grid.DaySum = b.Grid.DaySum || b.Grid.Day.reduce(sum);
return a.Grid.DaySum - b.Grid.DaySum;
});
console.log(input);

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