So for example I have object like this:
{
data: [
{
id: 13,
name: "id13"
},
{
id: 21,
name: "id21"
}
],
included: [
{
id: "13",
badge: true
},
{
id: "21",
badge: false
}
]
}
And now I need to loop over included and push included to data where id is equal.
So after transformation it would have badge in data, for example like this:
{
data: [
{
id: "13",
name: "id13",
included: {
id: "13",
badge: true
},
},
{
id: "21",
name: "id21",
included: {
id: "21",
badge: false
}
}
]
}
of course I tried on my own and I've created this code:
for(let i=0; i<includedLength; i++) {
console.log(a.included[i].id);
for(n=0; n<dataLength; n++) {
console.log(a.data[n]);
if(a.icluded[i].id === a.data[i].id) {
console.log('We have match!!!');
}
}
}
but it doesn't work I have an error in console
Uncaught TypeError: Cannot read property '0' of undefined
This is demo of my code.
All solutions here have gone in the same path as you had, which is not efficient. So I am posting my solution, which is more efficient than the other solutions so far. Read the code comments to understand the optimizations done.
// Convert data array into a map (This is a O(n) operation)
// This will give O(1) performance when adding items.
let dataMap = a.data.reduce((map, item) => {
map[item.id] = item;
return map;
}, {});
// Now we map items from included array into the dataMap object
// This operation is O(n). In other solutions, this step is O(n^2)
a.included.forEach(item => {
dataMap[item.id].included = item;
});
// Now we map through the original data array (to maintain the original order)
// This is also O(n)
let finalResult = {
data: a.data.map(({id}) => {
return dataMap[id];
})
};
console.log(JSON.stringify(finalResult))
Here is my solution, this will provide the required output!
It constains the same standard for loops.
Some points I would like to highlight are,
The id in included property is string, so you can use the + operator to convert it to number.
The Object.assign() method is used so that we create a new copy of the corresponding object. Read more here
var data = {
data: [{
id: 13,
name: "id13"
},
{
id: 21,
name: "id21"
}
],
included: [{
id: "13",
badge: true
},
{
id: "21",
badge: false
}
]
}
var output = {
data: data.data
};
for (var q of data.included) {
for (var j of output.data) {
if (+q.id === j.id) {
j['included'] = Object.assign({}, j);;
}
}
}
console.log(output);
.as-console {
height: 100%;
}
.as-console-wrapper {
max-height: 100% !important;
top: 0;
}
It seems like a waste of space to push the whole "included" element into the first array when a match is found (you really need that extra id element in there?) - so this just makes output like
[{id: 1, name: 'name', badge: true},{...}]
If no matching badge element is found, it sets badge to false.
var notJSON = {
data: [
{
id: 13,
name: "id13"
},
{
id: 21,
name: "id21"
}
],
included: [
{
id: "13",
badge: true
},
{
id: "21",
badge: false
}
]
};
var badged = notJSON.data.map(function (el, i) {
el.badge = notJSON.included.find(function (inc) {
return inc.id == el.id;
}).badge || false;
return el;
});
console.log(badged);
Its not a JSON, its an Object. A valid json consists of both its key and value as string. What you are trying to do is manipulate an object. The following code should help in getting the desired output.
const obj ={
data: [
{
id: 13,
name: "id13"
},
{
id: 21,
name: "id21"
}
],
included: [
{
id: "13",
badge: true
},
{
id: "21",
badge: false
}
]
}
for (var i=0; i<obj.data.length;i++){
for(var j=0; j< obj.included.length;j++){
if(obj.data[i].id == obj.included[j].id){
obj.data[i].included={
id: obj.included[j].id,
badge: obj.included[j].badge
}
}
}
}
delete obj.included
console.log(obj)
What I am doing her is:
Checking if id of obj.data is equal to that of obj.included
If they are equal add a new key called "included" in obj[data]
When the loop is over delete the "included" key from obj as its not required anymore.
var obj = {
data: [
{
id: 13,
name: "id13"
},
{
id: 21,
name: "id21"
}
],
included: [
{
id: "13",
badge: true
},
{
id: "21",
badge: false
}
]
};
obj.included.forEach((item) => {
obj.data.forEach(item1 => {
if(item.id == item1.id){
item1.included = item;
}
});
});
delete obj.included;
Related
I am trying to generate URLs for pages stored in a MongoDB in node.
Using the following function I want to traverse a javascript object that and display the path to each element.
I am nearly there, but I am stuck - There might even be a better way to do this using using Async (which I must admit, confuses me a bit).
Function: (demo)
function printTree(people, slug) {
for (var p = 0; p < people.length; p++) {
var root = people[p];
slug = slug + root.name + "/";
console.log(slug);
if (root.children.length > 0) {
var childrenCount = root.children.length;
for (var c = 0; c < childrenCount; c++) {
if (root.children[c].children.length > 0) {
printTree(root.children[c].children, slug + root.children[c].name + "/");
}
}
}
}
};
Output:
/michael/
/michael/angela/oscar
/michael/meredith/creed
/michael/meredith/creed/kelly
Expected Output:
/michael/
/michael/angela/
/michael/angela/oscar/
/michael/meredith/
/michael/meredith/creed/
/michael/meredith/kelly/
Object:
[
{
"name": "michael",
...
"children": [
{
"name": "angela",
...
"children": [
{
"name": "oscar",
...
"children": []
}
]
},
{
"name": "meredith",
...
"children": [
{
"name": "creed",
...
"children": []
},
{
"name": "kelly",
...
"children": []
}
]
},
{ ... }
]
}
]
If it helps, the data is stored using nested sets: https://github.com/groupdock/mongoose-nested-set
So there might be a better way to do the above work using nested sets (negating the above object).
Here you go. You don't need a second for loop, since your printTree function is going to loop through everything anyway (demo).
function printTree(people, slug){
slug = slug || '/';
for(var i = 0; i < people.length; i++) {
console.log(slug + people[i].name + '/');
if(people[i].children.length){
printTree(people[i].children, slug + people[i].name + '/')
}
}
}
You could also consider something in ECMA5 like this, in case you have further use of the tree or want to use some a seperator other than /. Nothing wrong with #bioball answer, this just gives you some more flexibility if wanted.
function makeTree(people, slug, sep) {
slug = slug || '/';
sep = sep || slug;
return people.reduce(function (tree, person) {
var slugPerson = slug + person.name + sep;
return tree.concat(slugPerson, makeTree(person.children, slugPerson, sep));
}, []);
}
function printTree(tree) {
tree.forEach(function (path) {
console.log(path);
});
}
printTree(makeTree(data));
On jsFiddle
Not a big fan of reinventing the wheel, so here is a solution using a object-scan. We use it for many data processing tasks and really like it because it makes things easier to maintain. However there is a learning curve. Anyways, here is how you could solve your question
// const objectScan = require('object-scan');
const scanTree = (tree) => objectScan(['**.children'], {
reverse: false,
breakFn: ({ isMatch, parents, context }) => {
if (!isMatch) {
return
}
context.push(
`/${parents
.filter((p) => 'name' in p)
.map(({ name }) => name)
.reverse()
.join('/')}/`
);
}
})(tree, []);
const tree = [{ id: '52fc69975ba8400021da5c7a', name: 'michael', children: [{ id: '52fc69975ba8400021da5c7d', parentId: '52fc69975ba8400021da5c7a', name: 'angela', children: [{ id: '52fc69975ba8400021da5c83', parentId: '52fc69975ba8400021da5c7d', name: 'oscar', children: [] }] }, { id: '52fc69975ba8400021da5c7b', parentId: '52fc69975ba8400021da5c7a', name: 'meredith', children: [{ id: '52fc69975ba8400021da5c7f', parentId: '52fc69975ba8400021da5c7b', name: 'creed', children: [] }, { id: '52fc69975ba8400021da5c7e', parentId: '52fc69975ba8400021da5c7b', name: 'kelly', children: [] }] }, { id: '52fc69975ba8400021da5c7c', parentId: '52fc69975ba8400021da5c7a', name: 'jim', children: [{ id: '52fc69975ba8400021da5c82', parentId: '52fc69975ba8400021da5c7c', name: 'dwight', children: [] }, { id: '52fc69975ba8400021da5c80', parentId: '52fc69975ba8400021da5c7c', name: 'phyllis', children: [] }, { id: '52fc69975ba8400021da5c81', parentId: '52fc69975ba8400021da5c7c', name: 'stanley', children: [] }] }] }];
scanTree(tree).map((e) => console.log(e));
// => /michael/
// => /michael/angela/
// => /michael/angela/oscar/
// => /michael/meredith/
// => /michael/meredith/creed/
// => /michael/meredith/kelly/
// => /michael/jim/
// => /michael/jim/dwight/
// => /michael/jim/phyllis/
// => /michael/jim/stanley/
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.8.0"></script>
Disclaimer: I'm the author of object-scan
Do you have an optimised solution for the following.
let say,
x = { users: [ {id: 1}, {id:2}, {id:3} ] }
I want to make a new key with same values, the output should be,
{ users: { list: [ {id: 1}, {id:2}, {id:3} ], count: 3 }
Using only JS or Underscore with one line code w/o any extra effort.
I know other tricks to do the same, but do we have one line solution for the same ?
Help appreciated...
Thanks.
Create the object, and assign the x array to list. However, count should be a getter, since the list length might change:
const x = { users: [{ id: 1 }, { id: 2 }, { id: 3 }] };
const result = {
users: {
list: x.users,
get count() { return this.list.length; }
}
};
console.log(JSON.stringify(result));
result.users.list.push({ id: 4 });
console.log(JSON.stringify(result));
I'm not sure why this is to be an optimised solution because this is a simple plain-JavaScript problem:
let x = { users: [{ id: 1 }, { id: 2 }, { id: 3 }] };
let result = {
users: {
list: x.users,
count: x.users.length
}
};
console.log(result);
Sure, just define the property as an object
const obj = {
users: [{
id: 1
}, {
id: 2
}, {
id: 3
}]
};
obj.users = { list: obj.users, count: obj.users.length };
console.log(obj);
I recommend focusing on code clarity rather than on line conservation though
I need to split an Array by its objects subvalue (type).
Let's assume I have following array:
[
{id:1,name:"John",information: { type :"employee"}},
{id:2,name:"Charles",information: { type :"employee"}},
{id:3,name:"Emma",information: { type :"ceo"}},
{id:4,name:"Jane",information: { type :"customer"}}
]
and I want to split the object by information.type so my final result looks like:
[
{
type:"employee",
persons:
[
{id:1,name:"John",information: { ... }},
{id:2,name:"Charles",information: { ... }
]
},
{
type:"ceo",
persons:
[
{id:3,name:"Emma",information: { ... }}
]
},
{
type:"customer",
persons:
[
{id:4,name:"Jane",information: { ... }}
]
},
]
Underscore is available at my Project. Any other helper library could be included.
Of course I could loop through the array and implement my own logic, but i was looking for cleaner solution.
This returns exactly what you want:
_.pairs(_.groupBy(originalArray, v => v.information.type)).map(p => ({type: p[0], persons: p[1]}))
A solution in plain Javascript with a temporary object for the groups.
var array = [{ id: 1, name: "John", information: { type: "employee" } }, { id: 2, name: "Charles", information: { type: "employee" } }, { id: 3, name: "Emma", information: { type: "ceo" } }, { id: 4, name: "Jane", information: { type: "customer" } }],
result = [];
array.forEach(function (a) {
var type = a.information.type;
if (!this[type]) {
this[type] = { type: type, persons: [] };
result.push(this[type]);
}
this[type].persons.push({ id: a.id, name: a.name });
}, {});
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
You could use the groupBy function of underscore.js:
var empList = [
{id:1,name:"John",information: { type :"employee"}},
{id:2,name:"Charles",information: { type :"employee"}},
{id:3,name:"Emma",information: { type :"ceo"}},
{id:4,name:"Jane",information: { type :"customer"}}
];
_.groupBy(empList, function(emp){ return emp.information.type; });
I am trying to generate URLs for pages stored in a MongoDB in node.
Using the following function I want to traverse a javascript object that and display the path to each element.
I am nearly there, but I am stuck - There might even be a better way to do this using using Async (which I must admit, confuses me a bit).
Function: (demo)
function printTree(people, slug) {
for (var p = 0; p < people.length; p++) {
var root = people[p];
slug = slug + root.name + "/";
console.log(slug);
if (root.children.length > 0) {
var childrenCount = root.children.length;
for (var c = 0; c < childrenCount; c++) {
if (root.children[c].children.length > 0) {
printTree(root.children[c].children, slug + root.children[c].name + "/");
}
}
}
}
};
Output:
/michael/
/michael/angela/oscar
/michael/meredith/creed
/michael/meredith/creed/kelly
Expected Output:
/michael/
/michael/angela/
/michael/angela/oscar/
/michael/meredith/
/michael/meredith/creed/
/michael/meredith/kelly/
Object:
[
{
"name": "michael",
...
"children": [
{
"name": "angela",
...
"children": [
{
"name": "oscar",
...
"children": []
}
]
},
{
"name": "meredith",
...
"children": [
{
"name": "creed",
...
"children": []
},
{
"name": "kelly",
...
"children": []
}
]
},
{ ... }
]
}
]
If it helps, the data is stored using nested sets: https://github.com/groupdock/mongoose-nested-set
So there might be a better way to do the above work using nested sets (negating the above object).
Here you go. You don't need a second for loop, since your printTree function is going to loop through everything anyway (demo).
function printTree(people, slug){
slug = slug || '/';
for(var i = 0; i < people.length; i++) {
console.log(slug + people[i].name + '/');
if(people[i].children.length){
printTree(people[i].children, slug + people[i].name + '/')
}
}
}
You could also consider something in ECMA5 like this, in case you have further use of the tree or want to use some a seperator other than /. Nothing wrong with #bioball answer, this just gives you some more flexibility if wanted.
function makeTree(people, slug, sep) {
slug = slug || '/';
sep = sep || slug;
return people.reduce(function (tree, person) {
var slugPerson = slug + person.name + sep;
return tree.concat(slugPerson, makeTree(person.children, slugPerson, sep));
}, []);
}
function printTree(tree) {
tree.forEach(function (path) {
console.log(path);
});
}
printTree(makeTree(data));
On jsFiddle
Not a big fan of reinventing the wheel, so here is a solution using a object-scan. We use it for many data processing tasks and really like it because it makes things easier to maintain. However there is a learning curve. Anyways, here is how you could solve your question
// const objectScan = require('object-scan');
const scanTree = (tree) => objectScan(['**.children'], {
reverse: false,
breakFn: ({ isMatch, parents, context }) => {
if (!isMatch) {
return
}
context.push(
`/${parents
.filter((p) => 'name' in p)
.map(({ name }) => name)
.reverse()
.join('/')}/`
);
}
})(tree, []);
const tree = [{ id: '52fc69975ba8400021da5c7a', name: 'michael', children: [{ id: '52fc69975ba8400021da5c7d', parentId: '52fc69975ba8400021da5c7a', name: 'angela', children: [{ id: '52fc69975ba8400021da5c83', parentId: '52fc69975ba8400021da5c7d', name: 'oscar', children: [] }] }, { id: '52fc69975ba8400021da5c7b', parentId: '52fc69975ba8400021da5c7a', name: 'meredith', children: [{ id: '52fc69975ba8400021da5c7f', parentId: '52fc69975ba8400021da5c7b', name: 'creed', children: [] }, { id: '52fc69975ba8400021da5c7e', parentId: '52fc69975ba8400021da5c7b', name: 'kelly', children: [] }] }, { id: '52fc69975ba8400021da5c7c', parentId: '52fc69975ba8400021da5c7a', name: 'jim', children: [{ id: '52fc69975ba8400021da5c82', parentId: '52fc69975ba8400021da5c7c', name: 'dwight', children: [] }, { id: '52fc69975ba8400021da5c80', parentId: '52fc69975ba8400021da5c7c', name: 'phyllis', children: [] }, { id: '52fc69975ba8400021da5c81', parentId: '52fc69975ba8400021da5c7c', name: 'stanley', children: [] }] }] }];
scanTree(tree).map((e) => console.log(e));
// => /michael/
// => /michael/angela/
// => /michael/angela/oscar/
// => /michael/meredith/
// => /michael/meredith/creed/
// => /michael/meredith/kelly/
// => /michael/jim/
// => /michael/jim/dwight/
// => /michael/jim/phyllis/
// => /michael/jim/stanley/
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.8.0"></script>
Disclaimer: I'm the author of object-scan
i have this json:
var projects_array = [
{name:"proj1", category:"a", index:1},
{name:"proj2", category:"a", index:2},
{name:"proj3", category:"b", index:3},
{name:"proj4", category:"c", index:4}
];
I am displaying them in order with something like this:
for (var i = 0 ; i <proj_num; i++){
var numClass="p_"+projects_array[i].index;
var act="<li class='"+numClass+"'></li>";
$('#lookfor').append(act);
}
What i need now is to display first all the objects with the value "a" in the category attribute and then all the other objects in any order, i tried using .sort() in this way:\
function compare(obj){
if(obj.category == "a"){ return 1;}
else{return 0;}
}
obj.sort(compare);
But it does not worked because it kind of group all the objects of the category...
do you have any possible solution for this problem?
ps: I tried to simplify my code to make it understandable, i hope it be clear for you, thanks in advance
this will sort according to the important word first.
http://jsbin.com/umezet/12/edit
var projects_array = [
{
name: "proj1",
category: "action",
index: 1
}, {
name: "proj2",
category: "time",
index: 2
}, {
name: "proj3",
category: "buu",
index: 3
}, {
name: "proj3",
category: "time",
index: 3
}, {
name: "proj4",
category: "cooo",
index: 4
}, {
name: "proj3",
category: "#",
index: 3
}, {
name: "proj1",
category: "!",
index: 1
}, {
name: "proj2",
category: "%",
index: 2
}, {
name: "proj3",
category: "cooo",
index: 3
}, {
name: "proj4",
category: "*",
index: 4
}];
projects_array = projects_array.sort(
function (x, y)
{
if(x['category'] == 'time') return -1;
if(y['category'] == 'time') return 1;
if(x['category'] > y['category']) return 1;
if(x['category'] < y['category']) return -1;
return 0;
});
for(var i = 0; i < projects_array.length; i++)
{
$('body').append(projects_array[i]['category']);
}