Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
My source array looks like this:
arr =[
{
"dimensionId": 1,
"dimensionName": "dimensionName1",
"components": [
{
"componentId": 1,
"componentName": "componentName1",
"indicators": [
{
"indicatorId": 1,
"indicatorName": "indicatorName1"
},
{...}
]
},
{...}
]
},
{...}
];
When I am trying to search the most deeply nested object by 'indicatorId' using .filter and .map , it return the object like this:
{
"indicatorId": 1,
"indicatorName": "indicatorName1"
}
What I need to do is keep track of the parent's properties as well when traversing through it's childrens. Desired output will be like this:
{
"dimensionId": 1,
"dimensionName": "dimensionName1",
"componentId": 1,
"componentName": "componentName2",
"indicatorId": 1,
"indicatorName": "indicatorName3"
}
Is there a way to do this recursively?
Edit: The id's are not unique throughout the array
You can loop through and return when found.
arr =[
{
"dimensionId": 1,
"dimensionName": "dimensionName1",
"components": [
{
"componentId": 2,
"componentName": "componentName2",
"indicators": [
{
"indicatorId": 3,
"indicatorName": "indicatorName3"
},
{
"indicatorId": 3.1,
"indicatorName": "indicatorName31"
},
]
}
]
},
];
let out = {}
arr.forEach(({dimensionId, dimensionName, components}) => {
components.forEach(({indicators, componentId, componentName}) => {
let found = indicators.find(({indicatorId}) => indicatorId === 3);
if (found) {
out = {dimensionId, dimensionName, componentId, componentName, ...found};
return;
}
});
})
console.log(out)
You could add the index to the keys form the same arrays and return a new object for each nested array.
function getFlat(array) {
return array.reduce((r, o, i) => {
Object
.entries(o)
.forEach(([k, v]) => Object.assign(r, Array.isArray(v)
? getFlat(v)
: { [[k, i].join('_')]: v }
));
return r;
}, {});
}
var data = [{ dimensionId: 1, dimensionName: "dimensionName1", components: [{ componentId: 2, componentName: "componentName2", indicators: [{ indicatorId: 3, indicatorName: "indicatorName3" }, { indicatorId: 4, indicatorName: "indicatorName4" }] }, { componentId: 5, componentName: "componentName5", indicators: [{ indicatorId: 6, indicatorName: "indicatorName6" }, { indicatorId: 7, indicatorName: "indicatorName8" }] }] }];
console.log(getFlat(data));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Related
So I have an array like this. Like array containing an array of objects.
posts = [
[
{
"id": 2,
"info": "This is some information"
},
{
"id": 3,
"info": "This is the other information"
}
],
[
{
"id": 2,
"info": "This is a duplicated id I want to remove"
},
{
"id": 4,
"info": "This information is safe"
}
]
]
I want to get the elements from each array and create a new array that only has the objects at the same time removing duplicated ids.
What am trying to achieve is something like
posts = [
{
"id": 2,
"info": "This is some information"
},
{
"id": 3,
"info": "This is the other information"
},
{
"id": 4,
"info": "This information is safe"
}
]
This is the code I have so far
id = ids.map(val => {
for(let i in val) {
console.log(val)
}
return something
})
I keep getting undefined values. I have tried forEach, for loop.
Any help would be much appreciated. Thanks
Use flat to get a flattened array of objects, and then loop over the array. If the current object's id can't be found in an object in the output array push that object.
const posts=[[{id:2,info:"This is some information"},{id:3,info:"This is the other information"}],[{id:2,info:"This is a duplicated id I want to remove"},{id:4,info:"This information is safe"}]];
const out = [];
for (const obj of posts.flat()) {
const found = out.find(f => obj.id === f.id);
if (!found) out.push(obj);
}
console.log(out);
You could use .flat() and then .filter():
const posts = [
[
{
id: 2,
info: 'This is some information',
},
{
id: 3,
info: 'This is the other information',
},
],
[
{
id: 2,
info: 'This is a duplicated id I want to remove',
},
{
id: 4,
info: 'This information is safe',
},
],
];
const newPosts = posts.flat().filter((x, i, self) => i === self.findIndex((y) => x.id === y.id));
console.log(newPosts);
Another potential (and more optimal) solution could be this using .reduce():
const posts = [
[
{
id: 2,
info: 'This is some information',
},
{
id: 3,
info: 'This is the other information',
},
],
[
{
id: 2,
info: 'This is a duplicated id I want to remove',
},
{
id: 4,
info: 'This information is safe',
},
],
];
const newPosts = Object.values(posts.flat().reduce((acc, curr) => {
return {
...acc,
...(!acc[curr.id] ? { [curr.id]: curr } : undefined),
};
}, {}));
console.log(newPosts);
Or, if you don't like .reduce(), you can do something very similar with the Map object and a for...of loop:
const posts = [
[
{
id: 2,
info: 'This is some information',
},
{
id: 3,
info: 'This is the other information',
},
],
[
{
id: 2,
info: 'This is a duplicated id I want to remove',
},
{
id: 4,
info: 'This information is safe',
},
],
];
const map = new Map();
for (const item of posts.flat()) {
if (map.has(item.id)) continue;
map.set(item.id, item);
}
const newPosts = Array.from(map.values());
console.log(newPosts);
Or even use a classic for loop to get the job done:
const posts = [
[
{
id: 2,
info: 'This is some information',
},
{
id: 3,
info: 'This is the other information',
},
],
[
{
id: 2,
info: 'This is a duplicated id I want to remove',
},
{
id: 4,
info: 'This information is safe',
},
],
];
const flattened = posts.flat();
const map = {};
for (let i = 0; i < flattened.length; i++) {
if (map[flattened[i].id]) continue;
map[flattened[i].id] = flattened[i];
}
console.log(Object.values(map));
Either way, in each of these examples we're following the same workflow:
Flatten the array so that all items are on the same level.
Filter out the items with the duplicate IDs.
I group by id in order to remove duplicates.
var posts = [[{id:2,info:"This is some information"},{id:3,info:"This is the other information"}],[{id:2,info:"This is a duplicated id I want to remove"},{id:4,info:"This information is safe"}]];
var agg = {}
posts.forEach(function(arr) {
arr.forEach(function(item) {
agg[item.id] = agg[item.id] || item
})
})
console.log(Object.values(agg))
.as-console-wrapper {
max-height: 100% !important
}
Flatten the array with flat, then use a set to keep track of the ids we already have. The ternary inside the filter is logic to check if the id is already in the set, and if it is, we filter the item out. Otherwise, we add the id back to the set.
const posts = [[{id:2,info:"This is some information"},{id:3,info:"This is the other information"}],[{id:2,info:"This is a duplicated id I want to remove"},{id:4,info:"This information is safe"}]];
const flat = posts.flat();
const ids = new Set();
const filtered = flat.filter((item) => ids.has(item.id) ? false : ids.add(item.id));
console.log(filtered);
.as-console-wrapper {
max-height: 100% !important
}
There are two things we need to do:
Flatten the inner areas into one main array with array.prototype.flat()
Remove duplicates based on, I'm assuming, the order of their appearance in the data.
We can do this by reducing the flattened array to an object with a condition that doesn't add any present id's if they're found
Then we convert that object to an array using Object.values()
let posts = [ [ { "id": 2, "info": "This is some information" }, { "id": 3, "info": "This is the other information" } ], [ { "id": 2, "info": "This is a duplicated id I want to remove" }, { "id": 4, "info": "This information is safe" } ] ]
let flattened = posts.flat()
console.log('Flattened: ', flattened)
let unique = flattened.reduce((acc, obj) => {
if (!acc[obj.id]) {
acc[obj.id] = obj
}
return acc
}, {})
console.log('Unique Objects: ', unique)
let result = Object.values(unique)
console.log('Final Array: ' ,result)
Doing it in one go and with a spread ... object merge:
let posts = [ [ { "id": 2, "info": "This is some information" }, { "id": 3, "info": "This is the other information" } ], [ { "id": 2, "info": "This is a duplicated id I want to remove" }, { "id": 4, "info": "This information is safe" } ] ]
let result = Object.values(
posts.flat().reduce((acc, obj) =>
({...{[obj.id]: obj}, ...acc})
, {})
);
console.log('Final Array: ', result);
The following example demonstrates what I would like to achieve:
const data_template = {
key: 1,
data_object: {
data1: 1,
data2: 2,
data3: 3
},
data_array: [
{ key: 1, data1: 1 },
{ key: 2, data1: 2 },
{ key: 3, data1: 3 }
]
}
let data = [];
data.push({ ...data_template, key: 1 });
data.push({ ...data_template, key: 2 });
console.log(data);
By using destructuring I can easily modify my "template object" (data_template) while adding instances of it to my "data array". The result of the code above is:
[
{
key: 1,
data_object: { data1: 1, data2: 2, data3: 3 },
data_array: [ [Object], [Object], [Object] ]
},
{
key: 2,
data_object: { data1: 1, data2: 2, data3: 3 },
data_array: [ [Object], [Object], [Object] ]
}
]
As you can see, there are 2 objects in my array (data), each with a unique key (1 and 2, respectively).
Now my question: How can I modify the keys within data_array, which is an array within my "data template"? Is there a smart way to do this by destructuring as well?
Depends on what you're trying to do.
If you simply want to clone the array with extra elements:
data.push({
...data_template,
key: 1,
data_array: [
...data_template.data_array,
{ key: 4, data1: 4},
],
});
If you want to e.g. add a new field to all the objects inside data_array:
const data_template = {
key: 1,
data_object: { data1: 1, data2: 2, data3: 3 },
data_array: [ { key: 1, data1: 1 }, { key: 2, data1: 2 }, { key: 3, data1: 3 }]
}
console.log({
...data_template,
key: 1,
data_array: data_template.data_array.map(d => ({ ...d, newField: 123 })),
});
which you could also use to overwrite the key field
Otherwise I can't immediately think of any other destructuring "tricks" you could use.
The short answer
No, you cant.
JavaScript destructuring only allows you to override root level keys of an object, which replaces their value.
(You also usually wouldn't do this, to keep cognitive complexity low.)
The long answer
There are actually better ways to do this.
Usually when creating a new object by spreading becomes more complex, you'd simply create a function for each level of nesting, and give it a better name than I did, that correlates to your domain.
This is exactly what functions are made for; to split pieces of code to reduce complexity.
Simple example
function createDataEntry(key, data) {
return { key, data }
}
function createRecord(key, data) {
return {
key,
dataObject: {
data,
},
dataArray: [
createDataEntry(key, data),
// or simply:
// { key, data },
]
}
}
let data = [];
data.push(createRecord(1, 'foo'));
data.push(createRecord(2, 'bar'));
console.log(JSON.stringify(data, null, 2));
produces
[
{
"key": 1,
"dataObject": {
"data": "foo"
},
"dataArray": [
{
"key": 1,
"data": "foo"
}
]
},
{
"key": 2,
"dataObject": {
"data": "bar"
},
"dataArray": [
{
"key": 2,
"data": "bar"
}
]
}
]
Add + Edit example
If you want to add or edit an entry from that data array, you'll have to do a check to see if an entry already exists.
If it already exists, update it using map.
If it does not exist, use array.push().
function addOrUpdateEntry(myObject, key, data) {
let updated = false;
const updatedObject = {
...myObject,
items: myObject.items.map(member => {
if (member.key === key) {
updated = true;
return { ...member, data };
}
return member;
})
};
if (!updated) {
updatedObject.items.push({ key, data });
}
return updatedObject;
}
let data = {
items: []
};
data = addOrUpdateEntry(data, 1, "foo");
data = addOrUpdateEntry(data, 2, "bar");
data = addOrUpdateEntry(data, 2, "baz");
console.log(JSON.stringify(data, null, 2));
Which will output
{
"items": [
{
"key": 1,
"data": "foo"
},
{
"key": 2,
"data": "baz"
}
]
}
Note that mutating an existing object can easily make for undesired behaviour in JavaScript, hence the creation of a new object and returning that from the function.
This concept is called immutability.
I have an object, which has 2 objects whithin and some properties of interest.
myObject = {
"chart":{
"data":[
{
"name":"First",
"x":[
"2000-01-01T00:00:00",
"2000-02-01T00:00:00",
"2000-03-01T00:00:00",
],
"y":[
1,
2,
3,
],
"type":"scatter"
},
{
"name":"Second",
"x":[
"2000-01-01T00:00:00",
"2000-02-01T00:00:00",
"2000-03-01T00:00:00",
],
"y":[
1,
2,
3,
],
"type":"scatter"
},
],
"otherKey":{
"whatever":"line"
}
},
};
I'm trying to create a new "object" which is an array of objects, and the only keys of interest are name and point, with point being an array of x and y combined.
newObject = [
{
"name":"First",
"point":[
["2000-01-01T00:00:00", 1],
["2000-02-01T00:00:00", 2],
["2000-03-01T00:00:00", 3],
],
},
{
"name":"Second",
"point":[
["2000-01-01T00:00:00", 1],
["2000-02-01T00:00:00", 2],
["2000-03-01T00:00:00", 3],
],
},
];
I'm trying to use map and filter function but I'm getting lost somewhere in the middle.
newObject = myObject.chart.data.map((value) => {
return [
value.x,
value.y
]
});
I think I need to concatenate them properly.
I'm a JS begginer, any tip or guidance would help, thanks.
you are missing how to combine those arrays of x and y, this implementation should help you
newObject = myObject.chart.data.map((value) => {
return {
name: value.name,
point: combine(value.x, value.y)
}
});
function combine(arr1, arr2) {
let ans = [];
for(let i = 0; i < arr1.length; i++) {
ans.push([arr1[i], arr2[i]])
}
return ans;
}
You can make use of map.
myObject = { "chart":{ "data":[ { "name":"First", "x":[ "2000-01-01T00:00:00", "2000-02-01T00:00:00", "2000-03-01T00:00:00", ], "y":[ 1, 2, 3, ], "type":"scatter" }, { "name":"Second", "x":[ "2000-01-01T00:00:00", "2000-02-01T00:00:00", "2000-03-01T00:00:00", ], "y":[ 1, 2, 3, ], "type":"scatter" }, ], "otherKey":{ "whatever":"line" } },};
result = myObject.chart.data.map(({name, ...rest})=>{
point = rest.x.map((k,i)=>[k,rest.y[i]]);
return {name, point};
});
console.log(result);
I have been playing around with some Nested Set Models (NSM). One thing I wanted to do is to be able to generate a NSM from a given JavaScript object.
For example, given the following object:
var data = {
Clothes: {
Jackets: {
Waterproof: true,
Insulated: true
},
Hats: true,
Socks: true
},
}
I'd like to generate an array of objects like so.
[
{
"name": "Clothes",
"lft": 1,
"rgt": 12
},
{
"name": "Jackets",
"lft": 2,
"rgt": 7
},
{
"name": "Waterproof",
"lft": 3,
"rgt": 4
},
{
"name": "Insulated",
"lft": 5,
"rgt": 6
},
{
"name": "Hats",
"lft": 8,
"rgt": 9
},
{
"name": "Socks",
"lft": 10,
"rgt": 11
}
]
That is - a depth first walk through the object, assigning an ID and counting the left and right edge for each object in the hierarchy. So that each node has a unique ID and the correct lft and rgt values for a NSM.
I've tried various approaches but just can't seem to get the result I am after...I had some success by altering the model to use properties for the node name and child nodes - i.e.
var data2 = {
name: "Clothes",
children: [{
name: "Jackets",
children: [{
name: "Waterproof",
}, {
name: "Insulated"
}]
}, {
name: "Hats"
},
{
name: "Socks"
}
]
};
function nestedSet(o, c, l = 0) {
let n = {
name: o.name,
lft: l + 1
};
c.push(n);
let r = n.lft;
for (var x in o.children) {
r = nestedSet(o.children[x], c, r);
}
n.rgt = r + 1;
return n.rgt;
}
let out = [];
nestedSet(data2, out);
console.log(out)
This gives the correct result but requires altering the input data...is there a way to generate the same Nested Set Model using the original data object?
I actually managed to solve this in the end...I just forgot about it for a long while! Basically all that is required is to reclusively pass the Object.entries as kindly suggested in #CherryDT's comment. This way one can resolve the name/children to build the nested set model as required.
var data = {
Clothes: {
Jackets: {
Waterproof: {},
Insulated: {},
},
Hats: {},
Socks: {},
},
};
function ns(node, stack = [], lft = 0) {
var rgt = ++lft;
var item = {
name: node[0],
lft: lft,
};
stack.push(item);
Object.entries(node[1]).forEach(function (c) {
rgt = ns(c, stack, rgt);
});
item.rgt = ++rgt;
return rgt;
}
var result = [];
ns(Object.entries(data)[0], result);
console.log(result);
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
I need to make addition using NodeJS in an array JSON and then return the altered JSON like this, I need also check if key exist or not to avoid exceptions:
JSON:
{
"cmd": [
{
"sales": [
{
"qte1": "2",
"qte2": "3",
"someString": "test
},
{
"qte1": "66",
"someAttribute": "test "
},
{
"qte2": "77",
"toto": "tata"
}
]
}
]
}
target JSON:
{
"cmd": [
{
"sales": [
{
"qte1": "2",
"qte2": "3",
"somme": "5"
},
{
"qte1": "66",
"somme": "66"
},
{
"qte2": "77",
"somme": "77"
}
]
}
]
}
I need to add the two key qte1 et qte2
would you have any propositions ?
Best regards
Seems like you just want the sum of the existing keys, should be pretty simple using a couple .map commands and a .reduce:
return json.cmd.map(function(salesList) {
return salesList.sales.map(function(sale) {
var keysToAdd = ["qte1", "qte2"];
sale.somme = Object.keys(sale).reduce(function(total, key) {
return total += keysToAdd.indexOf(key) > -1 ? +sale[key] : 0;
}, 0);
return sale;
});
});
Demo: https://jsfiddle.net/x294yt1h/
const data =
{
"cmd": [
{
"sales": [
{
"qte1": "2",
"qte2": "3"
},
{
"qte1": "66"
},
{
"qte2": "77"
}
]
}
]
};
function sum(dataObj) {
const hasCmd = dataObj && dataObj.cmd && dataObj.cmd.length > 0;
const hasSales = hasCmd && dataObj.cmd[0].sales && dataObj.cmd[0].sales.length > 0;
if (hasCmd && hasSales) {
const clonedArray = dataObj.cmd[0].sales.slice();
const summedArray = clonedArray.map(function(group) {
const groupKeys = Object.keys(group);
const sum = groupKeys.reduce(function (total, key) {
return total + parseInt(group[key], 10 /* Decimal Radix */);
}, 0);
return Object.assign({}, group, { 'somme': sum.toString() });
});
// build a new object to return to include new summed array
return { "cmd": [{ "sales": summedArray }] };
}
return dataObj;
}
console.log('data', JSON.stringify(data));
const newData = sum(data);
console.log('newData', JSON.stringify(newData));
Maybe you can first and formost refector your tree.
Like that :
{
command: [
{
sales: [
{
quantity: [1, 2, 4],
sum: 7
}
]
}
]
}
I don't know if your structure is open to any refectoring, but you must do something about it !
However, try to use the correct type of value in your row, and try to keep in mind to never mutate and object, use functor like map and reduce :
const cmd = [
{
sales: [
{
quantity: [1, 2, 3],
sum: 0
},
{
quantity: [67, 2, 3],
sum: 0
}
]
}
];
const newObjectWithSum = cmd.map(sales => {
return sales.sales.map((sale) => {
sale.sum = sale.quantity.reduce((valueIn, next) => {
return valueIn + next;
},0);
return sale;
});
})