My target is if the id from digital_assets and products matches then get the value of URL fro digital_assets and ProductName from products object. I'm able to traverse through the object and get the values of digital_assets and products but need some help to compare these two objects based on IDs to get the value of URL and ProductName. Below is what I've done so far.
var data = [{
"digital_assets": [{
"id": "AA001",
"url": "https://via.placeholder.com/150"
},{
"id": "AA002",
"url": "https://via.placeholder.com/150"
}]
}, {
"products": [{
"id": ["BB001", "AA001"],
"ProductName": "PROD 485"
},{
"id": ["BB002", "AA002"],
"ProductName": "PROD 555"
}]
}
];
$.each(data, function () {
var data = this;
//console.log(data);
$.each(data.digital_assets, function () {
var dAssets = this,
id = dAssets['id'];
// console.log(id);
});
$.each(data.products, function () {
var proData = this,
prod_id = proData['id'];
// console.log(prod_id);
$.each(prod_id, function () {
var arr_id = this;
console.log(arr_id);
});
});
});
Do I need to create new arrays and push the values into the new arrays? Then concat() these array to one. ? Bit lost any help will be appreciated.
Here is one way you can do this via Array.reduce, Array.includes, Object.entries and Array.forEach:
var data = [{ "digital_assets": [{ "id": "AA001", "url": "https://via.placeholder.com/150" }, { "id": "AA002", "url": "https://via.placeholder.com/150" } ] }, { "products": [{ "id": ["BB001", "AA001"], "ProductName": "PROD 485" }, { "id": ["BB002", "AA002"], "ProductName": "PROD 555" } ] } ]
const result = data.reduce((r,c) => {
Object.entries(c).forEach(([k,v]) =>
k == 'digital_assets'
? v.forEach(({id, url}) => r[id] = ({ id, url }))
: v.forEach(x => Object.keys(r).forEach(k => x.id.includes(k)
? r[k].ProductName = x.ProductName
: null))
)
return r
}, {})
console.log(Object.values(result))
You can use Array.prototype.find, Array.prototype.includes and Array.prototype.map to achieve this very gracefully.
let data = [
{
"digital_assets": [
{
"id": "AA001",
"url": "https://via.placeholder.com/150"
},
{
"id": "AA002",
"url": "https://via.placeholder.com/150"
}
]
},
{
"products": [
{
"id": ["BB001", "AA001"],
"ProductName": "PROD 485"
},
{
"id": ["BB002","AA002"],
"ProductName": "PROD 555"
}
]
}
];
// Find the 'digital_assets' array
let assets = data.find(d => d['digital_assets'])['digital_assets'];
// Find the 'products' array
let products = data.find(d => d['products'])['products'];
// Return an array of composed asset objects
let details = assets.map(a => {
return {
id : a.id,
url : a.url
name : products.find(p => p.id.includes(a.id)).ProductName
};
});
console.log(details);
changed answer to fit your needs:
var data = [
{
"digital_assets": [
{
"id": "AA001",
"url": "https://via.placeholder.com/150"
},
{
"id": "AA002",
"url": "https://via.placeholder.com/150"
}
]
},
{
"products": [
{
"id": ["BB001", "AA001"],
"ProductName": "PROD 485"
},
{
"id": ["BB002","AA002"],
"ProductName": "PROD 555"
}
]
}
]
let matchingIds = [];
let data_assetsObject = data.find(element => {
return Object.keys(element).includes("digital_assets")
})
let productsObject = data.find(element => {
return Object.keys(element).includes("products")
})
data_assetsObject["digital_assets"].forEach(da => {
productsObject["products"].forEach(product => {
if (product.id.includes(da.id)){
matchingIds.push({
url: da.url,
productName: product.ProductName
})
}
})
})
console.log(matchingIds);
working fiddle: https://jsfiddle.net/z2ak1fvs/3/
Hope that helped. If you dont want to use a new array, you could also store the respective data within the element you are looping through.
Edit:
I think i know why i got downvoted. My example works by making data an object, not an array. changed the snippet to show this more clearly.
Why is data an array anyway? Is there any reason for this or can you just transform it to an object?
Edit nr2:
changed the code to meet the expectations, as i understood them according to your comments. it now uses your data structure and no matter whats in data, you can now search for the objects containing the digital_assets / products property.
cheers
https://jsfiddle.net/2b1zutvx/
using map.
var myobj = data[0].digital_assets.map(function(x) {
return {
id: x.id,
url: x.url,
ProductName: data[1].products.filter(f => f.id.indexOf(x.id) > -1).map(m => m.ProductName)
};
});
Plunker
I have two structures - ingredients and recipes
[{
"id":"1",
"name": "Cucumber"
},
..
]
and
[{
"id":"1",
"name": "Salad1",
"recipein":[1, 3, 5]
}, {
...
}
]
and i want to show names of ingredients in each salad by press a button.
I filtered object to get ID of object, then i try to get a array of ingredients
getSalad(param:number) {
this.saladId = this.recipe.filter(rec => {
return rec.id.includes(param);
})
this.getNameOfIngredients(this.saladId)
}
getNameOfIngredients(saladArray:any) {
var ingredientsId = saladArray.map(function(num) {
return num.recipein;
});
i getting array [1,2,4] now i want to show all names of ingredients from this.ingredients with this array of id's.
How can i do this?
Plunker
I made updates in your plunker. I think thats what are you looking for: Plunker
getSalad(param:number) {
this.saladId = this.recipe.filter(rec => +rec.id === param )[0];
if(!this.saladId){
this.currentSalad = "Salad not found";
return;
}
this.currentSalad = this.getNameOfIngredients(this.saladId)
}
getNameOfIngredients(saladArray:any) {
return this.ingredients.filter( ing => {
return saladArray.recipein.indexOf(+ing.id) !== -1;
});
let _ingredients = []
this.ingredients.foreach((ingr)=>{
if(this.ingreIDArry.indexof(ingr.id) > -1){
_ingredients.push(ingr.name)
}
})
return _ingredients
is this what you want?
if you can flatten the array, it would be very straightforward for us to do lookups.
Here is what you could do.
const salads = [{
"id": "1",
"name": "Salad1",
"recipein": [1, 3, 5]
}];
const ingredients = [{
"id": "1",
"name": "Cucumber"
},
{
"id": "2",
"name": "Cucumber2"
},
{
"id": "3",
"name": "Cucumber3"
},
{
"id": "4",
"name": "Cucumber4"
},
{
"id": "5",
"name": "Cucumber5"
}
];
const flattenIngredients = (() => {
const output = {};
ingredients.forEach((ingredient) => {
output[ingredient.id] = ingredient;
});
return output;
})();
const getSalad = (saladId) => {
const filteredSalad = salads.filter((salad) => {
return saladId == salad.id;
});
if (filteredSalad.length > 0) {
const salad = filteredSalad[0];
return salad.recipein.map((receip) => flattenIngredients[receip].name);
}
}
console.log(getSalad(1));
I would like to merge 2 objects with the same properties into an Array.
Take this for an example:
object1 = {"id":1,
"name":name1,
"children":[{"id":2,"name":name2}]
};
object2 = {"id":3,
"name":name3,
"children":[{"id":4,"name":name4}]
};
object3 = {"id":1,
"name":name1,
"children":[{"id":6,"name":name6}]
};
var result = Object.assign(result,object1,object2,object3);
Expected result:
JSON.stringify([result]) =[
{"id":1,
"name":name1,
"children":[{"id":2,"name":name2},
{"id":6,"name":name6}]
},
{"id":3,
"name":name3,
"children":[{"id":4,"name":name4}]
}
]
Actual result:
JSON.stringify([result]) = [
{"id":3,
"name":name3,
"children":[{"id":4,"name":name4}]
}
]
Seems like Object.assign() isn't the way to go... as it will overwrite, I do not want it to overwrite, I want them to merge instead. Is there a right way to do this?
As so often, Array.prototype.reduce provides a good base for an approach like e.g. this one ...
var obj1 = {
"id": 1,
"name": "name1",
"children": [{ "id": 2, "name": "name2" }]
};
var obj2 = {
"id": 3,
"name": "name3",
"children": [{ "id": 4, "name": "name4" }]
};
var obj3 = {
"id": 1,
"name": "name1",
"children": [{ "id": 6, "name": "name6" }]
};
// Expected result: [{
// "id": 1,
// "name": name1,
// "children": [
// { "id": 2, "name": "name2" },
// { "id": 6, "name": "name6" }
// ]
// }, {
// "id": 3,
// "name": "name3",
// "children": [{"id": 4, "name": "name4" }]
// }]
function mergeEquallyLabeledTypes(collector, type) {
var key = (type.name + '#' + type.id); // identity key.
var store = collector.store;
var storedType = store[key];
if (storedType) { // merge `children` of identically named types.
storedType.children = storedType.children.concat(type.children);
} else {
store[key] = type;
collector.list.push(type);
}
return collector;
}
var result = [obj1, obj2, obj3].reduce(mergeEquallyLabeledTypes, {
store: {},
list: []
}).list;
console.log('result : ', result);
.as-console-wrapper { max-height: 100%!important; top: 0; }
Edit Note
After having been informed about changed requirements, that need to deal with a nested pattern, I will change my first provided approach into a generic solution. It will be not that difficult since there is a generically repeated pattern within the data structure. Thus I just need to make the already existing reducer function self recursive. A recursion step will be triggered after having finished a complete reducing cycle on any provided list ...
var obj1 = {
"id": 1,
"name": "name1",
"children": [{ "id": 2, "name": "name2", "children": [{ "id": 8, "name": "name8" }] }]
};
var obj2 = {
"id": 3,
"name": "name3",
"children": [{ "id": 4, "name": "name4", "children": [{ "id": 9, "name": "name9" }] }]
};
var obj3 = {
"id": 1,
"name": "name1",
"children": [{ "id": 6, "name": "name6", "children": [{ "id": 10, "name": "name10" }] }]
};
var obj4 = {
"id": 3,
"name": "name3",
"children": [{ "id": 4, "name": "name4", "children": [{ "id": 11, "name": "name11" }] }]
};
function mergeEquallyLabeledTypesRecursively(collector, type, idx, list) {
var key = (type.name + '#' + type.id); // identity key.
var store = collector.store;
var storedType = store[key];
if (storedType) { // merge `children` of identically named types.
storedType.children = storedType.children.concat(type.children);
} else {
store[key] = type;
collector.list.push(type);
}
// take repetitive data patterns into account ...
if (idx >= (list.length - 1)) {
collector.list.forEach(function (type) {
// ... behave recursive, when appropriate.
if (type.children) {
type.children = type.children.reduce(mergeEquallyLabeledTypesRecursively, {
store: {},
list: []
}).list;
}
});
}
return collector;
}
var result = [obj1, obj2, obj3, obj4].reduce(mergeEquallyLabeledTypesRecursively, {
store: {},
list: []
}).list;
console.log('result : ', result);
.as-console-wrapper { max-height: 100%!important; top: 0; }
This might be what your after, please note it's not recursive now recursive. But your example data doesn't appear to be anyway.
const object1 = {"id":1,
"name":"name1",
"children":[{"id":2,"name":"name2"}]
};
const object2 = {"id":3,
"name":"name3",
"children":[{"id":4,"name":"name4"}]
};
const object3 = {"id":1,
"name":"name1",
"children":[
{"id":6,"name":"name6"},
{"id":7,"name":"name7"},
{"id":6,"name":"name6"}
]
};
function merge(arr) {
const idLinks = {};
const ret = [];
arr.forEach((r) => {
if (!idLinks[r.id]) idLinks[r.id] = [];
idLinks[r.id].push(r);
});
Object.keys(idLinks).forEach((k) => {
const nn = idLinks[k];
const n = nn[0];
for (let l = 1; l < nn.length; l ++) {
if (nn[l].children) {
if (!n.children) n.children = [];
n.children = n.children.concat(nn[l].children);
}
}
if (n.children && n.children.length) n.children = merge(n.children);
ret.push(n);
});
return ret;
}
var result = merge([object1,object2,object3]);
console.log(result);
/* There are two cases :
a) No duplicate children
b) Duplicate children either in (same object || different object|| both)
*/
/* =============== */
/* Case a) */
const util = require('util');
var object1 = {
"id": 1,
"name": "name1",
"children": [{ "id": 2, "name": "name2" }]
};
var object2 = {
"id": 3,
"name": "name3",
"children": [{ "id": 4, "name": "name4" }]
};
var object3 = {
"id": 1,
"name":"name1",
"children":[{"id":6,"name":"name6"}]
};
var arr = [object1,object2,object3];
var uniqueIds = [];
var filteredArray = [];
var uniqueId='';
arr.map((item,i,array)=>{
uniqueId =uniqueIds.indexOf(item.id);
uniqueId = uniqueId+1;
uniqueIds = [...uniqueIds,item.id];
if(!uniqueId){
filteredArray[i] = item;
}
if(uniqueId){
filteredArray[uniqueId-1]['children'] = [...(array[uniqueId-1].children),...(item.children)];
}
});
console.log(util.inspect(filteredArray,false,null));
/* ============================================
Case b)
Dealing with the worst case of having duplicate children in both same
and different objects
*/
object1 = {"id":1,
"name":'name1',
"children":[{"id":2,"name":'name2'},
{"id":2,"name":'name2'}]
};
object2 = {"id":3,
"name":'name3',
"children":[{"id":4,"name":'name4'}]
};
object3 = {"id":1,
"name":'name1',
"children":[{"id":6,"name":'name6'},
{"id":7,"name":'name7'},
{"id":2,"name":'name2'}]
};
arr = [object1,object2,object3];
uniqueIds = [];
uniqueId = '';
arr.map((item,i,array)=>{
uniqueId =uniqueIds.indexOf(item.id);
uniqueId = uniqueId+1;
uniqueIds = [...uniqueIds,item.id];
if(!uniqueId){
filteredArray[i] = item;
}
if(uniqueId){
filteredArray[uniqueId-1]['children'] = [...(array[uniqueId-1].children),...(item.children)];
}
/*Removing duplicate children entries*/
filteredArray[uniqueIds.indexOf(item.id)]['children'] = filteredArray[uniqueIds.indexOf(item.id)]['children']
.filter((elem, index, self) => self.findIndex((t) => {return t.id === elem.id}) === index)
})
console.log(util.inspect(filteredArray,false,null));
In functional programming way with es6 standards. I am assuming children array also contains duplicates. I enclosed the code in closures.
See the following link why I used util to print all the object in node console.log()
How can I get the full object in Node.js's console.log(), rather than '[Object]'?
(function() {
'use strict';
const util = require('util');
/** string constants */
const ID = 'id';
const CHILDREN = 'children';
/* Objects to modify */
const object1 = {
"id": 1,
"name": "name1",
"children": [
{ "id": 2, "name": "name2" },
{ "id": 5, "name": "name5" },
{ "id": 7, "name": "name7" }
]
};
const object2 = {
"id": 3,
"name": "name3",
"children": [
{ "id": 4, "name": "name4" }
]
};
const object3 = {
"id": 1,
"name": "name1",
"children": [
{ "id": 5, "name": "name5" },
{ "id": 6, "name": "name6" }
]
};
/**
* Concates the arrays
* #param { array } - a
* #param { array } - b
*/
const merge = (a, b) => {
return a.concat(b);
};
/**
* Removes Duplicates from the given array based on ID
* #param { array } - array to remove duplicates
* #return { array } - array without duplicates
*/
const removeDuplicates = (arr) => {
return arr.filter((obj, pos, arr) => {
return arr.map((m) => {
return m[ID];
}).indexOf(obj[ID]) === pos;
});
}
/**
* Groups items in array with particular key
* Currying technique
* #param { prop } - key to group
* #return { () => {} } - Method which in turn takes array as argument
*/
const groupBy = (prop) => (array) => {
return array.reduce((groups, item) => {
const val = item[prop];
groups[val] = groups[val] || [];
groups[val].push(item);
return groups;
}, {});
}
/**
* Object containing grouped-items by particuar key
*/
const grouped = groupBy(ID)([object1, object2, object3]);
/**
* Removing the duplicates of children
* Remember map also mutates the array of objects key's value
* but not data type
*/
Object.keys(grouped).map((key, position) => {
grouped[key].reduce((a, b) => {
a[CHILDREN] = removeDuplicates(a[CHILDREN].concat(b[CHILDREN]));
});
});
/**
* Desired final output
*/
const final = Object.keys(grouped)
.map((key) => removeDuplicates(grouped[key]))
.reduce(merge, []);
console.log(util.inspect(final, false, null))})();
const object1 = {
"id":1,
"name":"name1",
"children":[{"id":2,"name":"name2"}]
};
const object2 = {
"id":3,
"name":"name3",
"children":[{"id":4,"name":"name4"}]
};
const object3 = {
"id":1,
"name":"name1",
"children":[{"id":6,"name":"name6"}]
};
var array = [object1,object2,object3];
var array2 = [object1,object2,object3];
function uniquearray(obj){
var result =[];
for(var i=0;i<array.length;i++){
if(obj.id == array[i].id){
result.push(array[i])
array.splice(i,1)
}
}
return result;
}
var arrayofuniarrays = []
for(var i=0;i<array2.length;i++){
arrayofuniarrays.push(uniquearray(array2[i]))
}
for(var i=0;i<arrayofuniarrays.length;i++){
for(var j=1;j<arrayofuniarrays[i].length; j++){
arrayofuniarrays[i][0].children.push(arrayofuniarrays[i][j].children)
arrayofuniarrays[i].splice(j,1)
}
}
var resul = arrayofuniarrays.reduce(function(a, b){return a.concat(b)},[])
console.log(resul)
Here is a sketch example of how to do this. It leverages a mapped type using your id as a key to ensure each item only appears once. It adds all of the children to an array based on the id.
If you needed to enforce the same behaviour on the children, you could use the same technique.
I have split this into multiple iterations to show you the individual parts in play.
Usually, it is more efficient to avoid creating objects that need to be zipped back up if you can.
const object1 = {
"id": 1,
"name": "name1",
"children": [{ "id": 2, "name": "name2" }]
};
const object2 = {
"id": 3,
"name": "name3",
"children": [{ "id": 4, "name": "name4" }]
};
const object3 = {
"id": 1,
"name":"name1",
"children":[{"id":6,"name":"name6"}]
};
const all = [object1, object2, object3];
// Use a map like a dictionary to enforce unique keys
const mapped = {};
for (let obj of all) {
if (!mapped[obj.id]) {
mapped[obj.id] = obj;
continue;
}
mapped[obj.id].children.push(obj.children);
}
console.log('Mapped ==> '+JSON.stringify(mapped));
// If you want to convert the mapped type to an array
const result = [];
for (let key in mapped) {
result.push(mapped[key]);
}
console.log('Array ==> '+JSON.stringify(result));
Building on #Peter Seliger's answer here, I derived with the following method to merge arrays with deeply nested children.
Given the following objects:
var obj1 = {
"id": 1,
"name": "name1",
"children": [{ "id": 2, "name": "name2", children:[{ "id":8, "name": "name8" }] }]
};
var obj2 = {
"id": 3,
"name": "name3",
"children": [{ "id": 4, "name": "name4", children:[{ "id":9, "name": "name9" }] }]
};
var obj3 = {
"id": 1,
"name": "name1",
"children": [{ "id": 6, "name": "name6", children:[{ "id":10, "name": "name10" }] }]
};
var obj4 = {
"id": 3,
"name": "name3",
"children": [{ "id": 4, "name": "name4", children:[{ "id":11, "name": "name11" }] }]
};
First we merge the parents
function mergeEquallyLabeledTypes(collector, type) {
var key = (type.name + '#' + type.id); // identity key.
var store = collector.store;
var storedType = store[key];
if (storedType) { // merge `children` of identically named types.
if(storedType.children)
storedType.children = storedType.children.concat(type.children);
} else {
store[key] = type;
collector.list.push(type);
}
return collector;
}
var result = [obj1, obj2, obj3, obj4].reduce(mergeEquallyLabeledTypes, {
store: {},
list: []
}).list;
Then we merge the children and subchildren if any.
for(let i=0; i<result.length; i++){
var children = result[i].children;
if(children){
var reducedChildren = children.reduce(mergeEquallyLabeledTypes, {store: {}, list: []}).list;
for(let j=0; j<reducedChildren.length; j++){
var subchildren = reducedChildren[j].children;
if(subchildren){
var reducedSubchildren = subchildren.reduce(mergeEquallyLabeledTypes, {store: {}, list: []}).list;
reducedChildren[j].children = reducedSubchildren;
}
}
result[i].children = reducedChildren;
}
}
Finally the result will be what I'll parse into my website.
console.log('result : ', result);
I am able to get the expected result.
// result: [{
// "id": 1,
// "name": name1,
// "children": [
// { "id": 2, "name": "name2", children:[{ "id":8, "name": "name8" }] },
// { "id": 6, "name": "name6", children:[{ "id":10, "name": "name10" }] }
// ]
// }, {
// "id": 3,
// "name": "name3",
// "children": [{"id": 4, "name": "name4", children:[
// { "id":9, "name": "name9" },
// { "id":11, "name": "name11" }
// ]
// }
// ]
// }]
However, this might not be too efficient as I'll need to keep adding on to the merging of children/subchildren method if my tree get nested with more levels. (e.g. subsubchildren, subsubsubchildren and so on...)
Is there any more efficient way to do this?
const object1 = {
id:1,
name:'a',
}
const object2 = {
id:3,
name:'b',
}
const object3 = {
id:1,
name:'c',
}
const originArr = [object1, object2, object3]
const idArr = [object1.id, object2.id, object3.id]
const newIdArr = []
for (let id of idArr) {
if (newIdArr.indexOf(id)) newIdArr.push(id)
}
const result = newIdArr.map(id => {
let names = []
for (obj of originArr) {
if (id === obj.id) names.push(obj.name)
}
return { id, names }
})
console.log(result)
http://jsfiddle.net/rw0z9e2j/
var sports = [{
"id": 1,
"name": "baseball"
}, {
"id": 2,
"name": "Football"
}];
var playersData = [{
"sport_id": 2,
"id": "nv12",
"name": "James"
}, {
"sport_id": 2,
"id": "nv11",
"name": "Jean"
}];
var arr = [],
tempObj = {};
$.each(sports, function (i, obj) {
var sport_id = obj.id;
$.each(playersData, function (i, obj) {
if (sport_id == obj.sport_id) {
tempObj = {
"sport_id": obj.sport_id,
"id": obj.id,
"name": obj.name
};
arr.push(tempObj);
}
});
obj.players = arr;
});
console.log(sports);
I try to build an array of players and put them within sports group according to sport_id but above logic has failed. It didn't group properly, the player who's in sport_id = 1 should go to sport which its id = 1 but why it didn't?
what's wrong with above loop there?
I suppose this is what you want:
var sports = [{
"id": 1,
"name": "baseball"
}, {
"id": 2,
"name": "Football"
}];
var playersData = [{
"sport_id": 2,
"id": "nv12",
"name": "James"
}, {
"sport_id": 2,
"id": "nv11",
"name": "Jean"
}];
sports.forEach(function (a) {
var arr = [];
playersData.forEach(function (b) {
if (a.id == b.sport_id) {
arr.push(b);
}
});
a.players = arr;
});
document.write('<pre>' + JSON.stringify(sports, 0, 4) + '</pre>');
You're declaring your temp vars outside of your loops, these should be scoped to your loops and thrown away after each operation.
var arr = [],
tempObj = {};
http://jsfiddle.net/samternent/rw0z9e2j/2/
You have to put it after push:
arr.push(tempObj);
obj.players = arr;
Actually you need this:
var sports = [{
"id": 1,
"name": "baseball"
}, {
"id": 2,
"name": "Football"
}];
var playersData = [{
"sport_id": 2,
"id": "nv12",
"name": "James"
}, {
"sport_id": 2,
"id": "nv11",
"name": "Jean"
}];
var arr = [];
$.each(sports, function (i, obj) {
$.each(playersData, function (i, player) {
if (obj.id === player.sport_id) {
var tempObj = {
"sport_id": player.sport_id,
"id": player.id,
"name": player.name
};
arr.push(tempObj);
obj.players = arr;
}
});
});
console.log(sports);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Hope you want to put inside the Sports group, but you are adding inside the player array, please notice, so please call
obj.sports = arr;
Hope it solve your problem .