how to convert array of object into object? - javascript

I have an array of object and i cant convert it into an object. The array of object that i have is as below:
var x = [{
name: "a",
items: [{
name: "b",
items: [{
name: "c"
}]
}]
},
{
name: "d",
items: [{
name: "e",
items: [{
name: "f"
}]
}]
}];
and i want to convert it into object like this:
{
name: "a",
items: [{
name: "b",
items: [{
name: "c"
}]
}]
},{
name: "d",
items: [{
name: "e",
items: [{
name: "f"
}]
}]
}
I used one of the stackoverflow solution
function toObject(arr) {
var rv = {};
for (var i = 0; i < arr.length; ++i)
rv[i] = arr[i];
return rv;
}
But it convert as follows:
{
"0":{
"name":"a",
"items":[{"name":"b","items":[{"name":"c"}]}]},
"1":{
"name":"d",
"items":[{"name":"e","items":[{"name":"f"}]}]}
}
So, how to get the object in the format i gave?

The following is not one object
{
name: "a",
items: [{
name: "b",
items: [{
name: "c"
}]
}]
}, // this comma shows you that it's can't be one object.
{
name: "d",
items: [{
name: "e",
items: [{
name: "f"
}]
}]
}
You can't have an object with the two different properties of the same name.

You want to reduce the array into a single object:
var output = x.reduce(function(output, arrayItem) {
var key = arrayItem.name;
output[key] = arrayItem;
return output;
}, { });

maybe you want something like this
var x = [{
name: "a",
items: [{
name: "b",
items: [{
name: "c"
}]
}]
},
{
name: "d",
items: [{
name: "e",
items: [{
name: "f"
}]
}]
}];
function toObject(arr) {
var rv = {};
for (var i = 0; i < arr.length; ++i)
rv[arr[i].name] = { items: arr[i].items} ;
return rv;
}
console.log(toObject(x));
http://jsfiddle.net/jypahwj4/

One problem that your doing to run into is that when creating an object from that array you will have multiple keys with the same name so each name: will be overridden by the next one you try and add to the object. In your function because you are doing rv[i] and i is a number that is why you are getting the numbers in your newly created object.
A possible solution would be
function toObject(arr) {
var rv = {};
for (var i = 0; i < arr.length; ++i)
rv[arr[i].name] = arr[i].items;
return rv;
}
But this would make the key of the outer part of the object be equal to the name: of the original array of objects

Related

Find all occurance of keys in the object and change the value

I want to find all occurances of keys and change its value. I want the id and name to be changed in the entire object to a new value.
const myObj = {
id: 1,
name: "a",
children: [
{
id: 2,
name: "b",
children: [
{
id: 3,
name: "c",
}
]
},
{
id: 4,
name: "d",
children: [
{
id: 5,
name: "e",
children: [
{
id: 6,
name: "f",
children: [
{
id: 7,
name: "g",
}
]
}
]
}
]
},
]
}
Code which i tried, not sure how can i get two properties and change their values.
function findAllByKey(obj, id, name) {
let constObj = Object.entries(obj);
console.log(obj)
Object.entries(constObj)
.reduce(([key, value]) => (key === id)
? obj[key] = "123"
: (typeof value === 'object'))
return obj;
}
// USAGE,
console.log(findAllByKey(myObj, 'id', 'name'))
You will need to utilize recursion for this.
Dave Newton gives a good argument in saying that the children properties are the ones containing most of the properties you want to change, so I would recommend you loop through each of the children properties and change their ID in that way. However, the answer I give you will work in more generic instances (for those who may need a different approach).
function findAllByKeyAndReplace(object,key,replace){
const looper = function(obj){
for (let k in obj){
if(k === key){
obj[k] = replace;
}
else if("object" === typeof obj[k]){
looper(obj[k]);
}
}
}
looper(object);
}
const myObj = {
id: 1,
name: "a",
children: [
{
id: 2,
name: "b",
children: [
{
id: 3,
name: "c",
}
]
},
{
id: 4,
name: "d",
children: [
{
id: 5,
name: "e",
children: [
{
id: 6,
name: "f",
children: [
{
id: 7,
name: "g",
}
]
}
]
}
]
},
]
}
findAllByKeyAndReplace(myObj,"id","123");
findAllByKeyAndReplace(myObj,"name","pickles");
console.log(myObj);
Note that I am pretty old school about this so please tell me if there is any way to improve on this, as it does take up a bit of power.
Oh wait, I see you want to return a list of the objects and not bulk-replace them with one value. Ok, that is quite easily done, too.
Instead of changing them just add them to an array which will be returned on the looper being finished:
function findAllByKey(object, key) {
const collection = [];
const looper = function(obj) {
for (let k in obj) {
if (k === key) {
collection.push(obj[k]);
} else if ("object" === typeof obj[k]) {
looper(obj[k]);
}
}
}
looper(object);
return collection;
}
const myObj = {
id: 1,
name: "a",
children: [{
id: 2,
name: "b",
children: [{
id: 3,
name: "c",
}]
},
{
id: 4,
name: "d",
children: [{
id: 5,
name: "e",
children: [{
id: 6,
name: "f",
children: [{
id: 7,
name: "g",
}]
}]
}]
},
]
}
console.log(findAllByKey(myObj, "id"));
console.log(findAllByKey(myObj, "name"));

Populate array compared to another array

I have these 2 arrays
var array1 = [{
id: "Test1",
value: "Beep"
}, {
id: "Test2",
value: "Meep"
}, {
id: "Test3",
value: "Sheep"
}];
var array2 = [{
id: "Test2"
}, {
id: "Test3"
}],
stuff2: [{
id: "Test1"
}, {
id: "Test3"
}],
stuff3: [{
id: "Test3"
}];
I am looking for a way to insert empty objects or null to the 2nd array so that the 2nd array looks something like this:
var array2 = [null, {
id: "Test2"
}, {
id: "Test3"
}],
stuff2: [{
id: "Test1"
}, null, {
id: "Test3"
}],
stuff3: [
null, null, {
id: "Test3"
}
];
This can't be ES6 since the code base I am working on it rather old (AngularJS 1.4).
I haven't been able to wrap my mind around this one. Any help is welcome.
I am making some assumptions about what you are actually trying to accomplish (based on your input and desired output). The idea is that for each key in the containing object (array as you called them), you check to see if the id of each element in the stuff, stuff2, etc. arrays is equal to the object's index in the array + 1. (i.e. stuff[0].id === "Test1"). If that is not the case, insert null into the array at that position and move forward. It would look like the following:
var array2 = {
stuff: [{
id: "Test2"
}, {
id: "Test3"
}],
stuff2: [{
id: "Test1"
}, {
id: "Test3"
}],
stuff3: [{
id: "Test3"
}]
};
function fillGaps(obj) {
var keys = Object.keys(obj);
keys.forEach(function (key) {
var arr = obj[key];
for(var i = 0; i < arr.length; i++) {
var id = arr[i].id;
if (id !== "Test" + (i+1)) {
arr.splice(i, 0, null);
}
}
});
return obj;
}
console.log(fillGaps(array2));

filter nested tree object without losing structure

I have nested tree object I would like filter through without losing structure
var items = [
{
name: "a1",
id: 1,
children: [{
name: "a2",
id: 2,
children: [{
name: "a3",
id: 3
}]
}]
}
];
so for example if id == 2 remove object with id 2 and his children
if id == 3 only remove object with id 3
this's just apiece of object to make question clean but the object it self contains more and more :)
using vanilla javascript, _lodash or Angular2 it's okay
thank you
You can create recursive function using filter() and also continue filtering children if value is Array.
var items = [{
name: "a1",
id: 1,
children: [{
name: "a2",
id: 2,
children: [{
name: "a3",
id: 3
}, ]
}]
}];
function filterData(data, id) {
var r = data.filter(function(o) {
Object.keys(o).forEach(function(e) {
if (Array.isArray(o[e])) o[e] = filterData(o[e], id);
})
return o.id != id
})
return r;
}
console.log(filterData(items, 3))
console.log(filterData(items, 2))
Update: As Nina said if you know that children is property with array you don't need to loop keys you can directly target children property.
var items = [{
name: "a1",
id: 1,
children: [{
name: "a2",
id: 2,
children: [{
name: "a3",
id: 3
}, ]
}]
}];
const filterData = (data, id) => data.filter(o => {
if (o.children) o.children = filterData(o.children, id);
return o.id != id
})
console.log(JSON.stringify(filterData(items, 3), 0, 2))
console.log(JSON.stringify(filterData(items, 2), 0, 2))
If it's ok for your case to use Lodash+Deepdash, then:
let filtered = _.filterDeep(items,(i)=>i.id!=3,{tree:true});
Here is a demo Codepen
You could use an iterative approach with Array#some and call the callback iter recursive for the children. I found, splice.
function deleteItem(id) {
items.some(function iter(a, i, aa) {
if (a.id === id) {
aa.splice(i, 1);
return true;
}
return a.children.some(iter);
});
}
var items = [{ name: "a1", id: 1, children: [{ name: "a2", id: 2, children: [{ name: "a3", id: 3 }] }] }];
console.log(items);
deleteItem(3);
console.log(items);
deleteItem(2);
console.log(items);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Use recursive function:
var items = [
{
name: "a1",
id: 1,
children: [{
name: "a2",
id: 2,
children: [{
name: "a3",
id: 3,
children: [{
name: "a4",
id: 4,
}]
}]
}]
}
];
function filterId(items, id) {
var len = items.length;
while (len--) {
if (items[len].id === id) {
items.splice(len, 1);
break;
} else {
filterId(items[len].children, id);
}
}
return items;
}
// filtering out the item with 'id' = 4
console.log(filterId(items, 4));
// filtering out the item with 'id' = 2
console.log(filterId(items, 2));

Merge nested array in object in Javascript

I need to merge 2 objects with nested arrays
var dest = {
id: "865",
arr: [{
id: "123",
value: "First" }]
};
var src = {
id: "865",
arr: [{
id: "456",
value: "Second" }]
};
to produce
merge = {
id: "865",
arr: [{id: "123",
value: "First"},
{id: "456",
value: "Second"}]
};
I tried using _.merge(dest, src) (using Lodash) and a couple of other methods, but seems like the 2nd object is overwriting the first one because it doesn't handle the nested array the way I want.
What is the best way to do this?
Thanks,
You can use Lodash _.mergeWith method:
var dest = {
id: "865",
arr: [{
id: "123",
value: "First"
}]
};
var src = {
id: "865",
arr: [{
id: "456",
value: "Second"
}]
};
var merge = _.mergeWith({}, src, dest, function(a, b) {
if (_.isArray(a)) {
return b.concat(a);
}
});
console.log(merge);
It allows you to pass a customizer in order to merge the array in a "custom" way.
Here's the fiddle. Hope it helps.
You can use Object.assign()
var dest = {
id: "865",
arr: [{
id: "123",
value: "First" }]
};
var src = {
id: "865",
arr: [{
id: "456",
value: "Second" }]
};
var merge = Object.assign({}, dest);
merge.arr.push(Object.assign({}, src.arr[0]));
src.arr[0].id = 789; // should not affect `merge.arr`
console.log(merge);
Without any libraries.
var dest = {
id: "865",
arr: [{
id: "123",
value: "First"
}]
};
var src = {
id: "865",
arr: [{
id: "456",
value: "Second"
}]
};
// 1
var resultOne = {
id: dest.id,
arr: src.arr.concat(dest.arr)
};
// 2
var resultTwo = Object.assign({}, src, {
arr: src.arr.concat(dest.arr)
});
// 3
var merge = function(obj1, obj2) {
return Object.keys(obj1).reduce(function(result, next) {
if (Array.isArray(obj1[next]) && Array.isArray(obj2[next])) {
result[next] = obj1[next].concat(obj2[next]);
} else if (obj1[next] && obj2[next]) {
result[next] = obj2[next];
}
return result;
}, {});
}
console.log(merge(src, dest));

Flatten 3D array containing objects to 2D removing duplicated objects by it's parameter

I have a 3D array with objects inside:
[
[{ id: 1 }, { id: 2 }],
[{ id: 3 }],
[{ id: 3 }, { id: 4 }]
]
How to flatten it including removing duplicated id parameter?
[{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }]
I think underscore would be helpful with that
var a = [
[{ id: 1 }, { id: 2 }],
[{ id: 3 }],
[{ id: 3 }, { id: 4 }]
];
var flattened = _(a).flatten().uniq('id').value();
Of course you have to include lodash to your webpage.
You can use Underscore flatten and unique to accomplish this. However, whenever you are using multiple underscore operations, it is a good time to consider using the underscore chainging with chain and value:
var data = [
[{ id: 1 }, { id: 2 }],
[{ id: 3 }],
[{ id: 3 }, { id: 4 }]
];
var result = _.chain(data)
.flatten()
.uniq(function(o) {
return o.id;
})
.value();
console.log('result', result);
JSFiddle: http://jsfiddle.net/0udLde0s/3/
Even shorter with current Underscore.js
If you use a recent version of Underscore.js (I tried current which is 1.8.3 right now), you can use .uniq('id') so it makes it even shorter:
var result = _.chain(data)
.flatten()
.uniq('id')
.value();
You can use _.flatten, and _.uniq, like so
var data = [
[{ id: 1 }, { id: 2 }],
[{ id: 3 }],
[{ id: 3 }, { id: 4 }]
];
var result = _.uniq(_.flatten(data), function (el) {
return el.id;
});
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
You don't need any library for this, it's quite simple:
function flatten(arr)
{
var map = {};
var flatArray = [];
function pushToMap(o) {
if(map[o.id])
return;
map[o.id] = true;
flatArray.push(o);
}
function arrRecurse(a) {
if(a.length === undefined)
pushToMap(a);
else {
a.forEach(function(i) {
arrRecurse(i);
});
}
}
arrRecurse(arr);
return flatArray;
}
var _3dArray = [
[{ id: 1 }, { id: 2 }],
[{ id: 3 }],
[{ id: 3 }, { id: 4 }]
];
alert(JSON.stringify(flatten(_3dArray)));
No library, only native JS :
var ar = [
[{ id: 1 }, { id: 2 }],
[{ id: 3 }],
[{ id: 3 }, { id: 4 }]
];
//start
var output = [];
for (var x = 0, al = {}; x < ar.length; x++)
for (var y = 0, t = ar[x][y]; y < ar[x].length; y++, t = ar[x][y])
al[t.id] = (!al[t.id]) ? output.push(t) : 1;
//end
document.body.innerHTML += JSON.stringify(output);

Categories

Resources