I have a data structure that looks like this
const array = [{
name: 'bar',
children: [{
name: 'foo',
children: [{
name: 'baz123',
}, {
name: 'baz',
}]
}]
}, {
name: 'shallowKey'
}, {
name: 'abc'
}];
And I would like to flatten it to look something like this
[{
name: 'bar'
}, {
name: 'foo',
}, {
name: 'baz123',
}, {
name: 'baz',
}, {
name: 'shallowKey'
}, {
name: 'abc'
}];
I tried lodash like this https://jsfiddle.net/hmzhjji/081q60qg/1/
But it's not doing anything, any other way I can do this?
Thanks
A recursive way would be:
function flatten(array, result = []){
for(const {name, children} of array){
result.push({name});
if(children) flatten(children, result);
}
return result;
}
Or the alternative ES6 version:
const flatten = array => array.reduce((res, {name, children = []}) => res.concat(name).concat(flatten(children)), []);
So you can do flatten(array) to get the desired result.
You can use forEach to iterate over the array and check if the required object is present and call the function recursively
const array = [{
name: 'bar',
children: [{
name: 'foo',
children: [{
name: 'baz123',
}, {
name: 'baz',
}]
}]
}, {
name: 'shallowKey'
}, {
name: 'abc'
}];
var res = [];
function flatten(array){
array.forEach(function(obj){
var name = {name: obj.name}
res.push(name);
if(obj.children){
flatten(obj.children)
}
})
return res;
}
console.log(flatten(array))
Related
I am trying to remove all the _er and _bx from the array, how can I do it? The way I tried doesn't seem to work. I'd like to see a solution where it removes all after _, and aswell only the letter that I put in for e.g remove all _ with er after.
const nullValue = {
collection: [{
name: "test_er"
},
{
name: "test_bx"
},
{
name: "fred"
},
{
name: "test_er"
}
]
};
const newArr = []
for (let [key, item] of nullValue.collection.entries()) {
item.name.replace(/_er/g, '')
newArr.push(item)
}
console.log(newArr)
Is this what you're looking for?
const nullValue = {
collection: [
{
name: 'test_er',
},
{
name: 'test_bx',
},
{
name: 'fred',
},
{
name: 'test_er',
},
],
};
nullValue.collection = [
...nullValue.collection.map(item => ({
name: item.name.replace(/_.*$/, ''),
})),
];
console.log(nullValue);
You can also use .split('_')[0] with the map method similar to Dmitry's answer... This gives you the first string of the split array, split at the underscore...
const nullValue = {
collection: [{
name: "test_er"
},
{
name: "test_bx"
},
{
name: "fred"
},
{
name: "test_er"
}
]
};
nullValue.collection = [ ...nullValue.collection.map( names => ({ name: names.name.split('_')[0], })),]
console.log(nullValue)
If you want to keep the original array of objects...
const nullValue = {
collection: [{
name: "test_er"
},
{
name: "test_bx"
},
{
name: "fred"
},
{
name: "test_er"
}
]
};
const newArr = { collection :
[ ...nullValue.collection.map( names =>
({ name: names.name.split('_')[0], })),
]}
console.log('newArr = ', newArr)
console.log('nullValue = ', nullValue)
You were VERY close with your original code, but the mistake was that String.replace() does not operate in-place, but rather returns its result. I've modified your code and added a comment below:
const nullValue = {
collection: [{
name: "test_er"
},
{
name: "test_bx"
},
{
name: "fred"
},
{
name: "test_er"
}
]
};
const newArr = []
for (let [key, item] of nullValue.collection.entries()) {
// My change is here
newArr.push( item.name.replace(/_er/g, '') )
}
console.log(newArr)
const nullValue = {
collection: [
{
name: "test_er"
},
{
name: "test_bx"
},
{
name: "fred"
},
{
name: "test_er"
}
]
};
nullValue.collection = nullValue.collection.map(i => i.name.replace(/_.*$/, ''))
console.log(nullValue)
This is preferable to .map() since you don't need a new array. You just want to change the strings:
const nullValue = {
collection: [
{ name: "test_er" },
{ name: "test_bx" },
{ name: "fred" },
{ name: "test_er" }
]
};
nullValue.collection.forEach(i => i.name = i.name.replace(/_.*$/, ''));
console.log(nullValue.collection);
I wanted to update the object when it pass the check but getting different result then expected.
What is preferred way to get the expected?
This the sample data:
var abc = [
{
type: "manager",
members: [{name: 'bob'}, {name: 'rob'}]
},
{
type: "clerk",
members: [{name: 'foo'}, {name: 'bar'}]
}
];
Using this function :
function funn() {
return abc.map((cate) => {
return cate.members.map((mem) => {
if (mem.name === 'bob') {
mem['isBob'] = true;
}
return mem;
});
});
}
I wanted in this format (expected):
[
{
type: 'manager',
members: [{ name: 'bob', isBob: true }, { name: 'rob' }],
},
{
type: 'clerk',
members: [{ name: 'foo' }, { name: 'bar' }],
},
];
The actual is like this:
[
{
members: [{ name: 'bob' }, { name: 'rob' }],
},
{
members: [{ name: 'foo' }, { name: 'bar' }],
},
];
You just need to find the element in the members array and if it is present then add the isBob property.
var abc = [{
type: "manager",
members: [{
name: "bob"
}, {
name: "rob"
}],
},
{
type: "clerk",
members: [{
name: "foo"
}, {
name: "bar"
}],
},
];
const result = abc.map((obj) => {
const isExist = obj.members.find((o) => o.name === "bob");
if (isExist) isExist.isBob = true;
return obj;
});
console.log(result);
The output you showed for your code is not matching when i run it.
Here I have modified your code a bit:
Instead of returning an array from the outer .map(), I am returning an object which has a type and members category.
function funn() {
return abc.map((cate) => {
let cateMembers = cate.members.map((mem) => {
if (mem.name === 'bob') {
mem['isBob'] = true;
}
return mem;
});
return { type : cate.type , members : cateMembers};
});
}
Remember to check like this : console.log(funn())
You could do something like this:
var abc = [
{
type: "manager",
members: [{name: 'bob'}, {name: 'rob'}]
},
{
type: "clerk",
members: [{name: 'foo'}, {name: 'bar'}]
}
];
function funn() {
abc.forEach(el => {
el.members.forEach(el2 => {
if(el2.name === 'bob') el2.isBob = true
})
})
return abc
}
console.log(funn())
I'm wondering, I have the following data structure:
data = [
{
name: 'Alpha',
},
{
name: 'Alfa',
},
{
name: 'Bravo',
},
{
name: 'Brafo',
},
{
name: 'Charlie',
},
{
name: 'Charly',
},
...
{
name: 'Zulu',
},
{
name: 'Zulo',
},
]
I'm expecting there to be at least one, usually more, key for each letter of the alphabet. However, if there isn't a single data.name I would still like in the below data structure to have an empty domains array [].
I was wondering, how could this be manipulated into the following data structure:
data = {
a: {
domains: [
{
name: 'Alpha',
},
{
name: 'Alfa',
},
],
},
b: {
domains: [
...
]
},
...
z: {
domains: [
...
]
},
};
I have used a few methods, which involved a pre-constructed "alphbetised" key = object array, then filtered each on the first letter of the data.name value...but I was wondering if there was a standard and performant method to acheive this?
Using reduce()
const data = [{name:"Alpha"},{name:"Alfa"},{name:"Bravo"},{name:"Brafo"},{name:"Charlie"},{name:"Charly"},{name:"Zulu"},{name:"Zulo"}]
const res = data.reduce((a, v) => {
// prepare key
let key = v.name.substring(0,1).toLowerCase()
// check key in accumulator
if (!a[key]) {
// assign domain object
a[key] = {domains: []}
}
// push domain array
a[key].domains.push(v)
return a
}, {})
console.log(res)
Here is what you want:
data = [
{
name: 'Alpha',
},
{
name: 'Alfa',
},
{
name: 'Bravo',
},
{
name: 'Brafo',
},
{
name: 'Charlie',
},
{
name: 'Charly',
},
{
name: 'Zulu',
},
{
name: 'Zulo',
},
];
console.log(data.reduce((a, c) => {
const firstLetter = c.name[0].toLowerCase();
if (a[firstLetter]) {
a[firstLetter].domains.push(c);
} else {
a[firstLetter] = { domains: [c] };
}
return a;
}, {}));
Suppose there are two objects.
const a = [
{ id: '1-1-1', name: 'a111' },
{ id: '1-1-2', name: 'a112' },
{ id: '1-2-1', name: 'a121' },
{ id: '1-2-2', name: 'a122' },
{ id: '2-1-1', name: 'a211' },
{ id: '2-1-2', name: 'a212' }
]
const b = ['1-1', '1-2', '2-1']
and the result
{
'1-1':[
{ id: '1-1-1', name: 'a111' },
{ id: '1-1-2', name: 'a112' },
],
'1-2':[
{ id: '1-2-1', name: 'a121' },
{ id: '1-2-2', name: 'a122' },
],
'2-1':[
{ id: '2-1-1', name: 'a211' },
{ id: '2-1-2', name: 'a212' },
]
}
Basically, I want to group the data.
I use includes to check if the item from b to match the id from a. Then construct the new array.
This is my attempt(fiddle):
return b.map(item => a.map(jtem => {
if(jtem.id.includes(item)){
return {
[item]: jtem
}
}
}))
For somehow, it doesn't work.
and, is there a clever way to avoid the nested for loop or map function?
You can do that in following steps:
Apply reduce() on the array b
During each iteration use filter() on the the array a
Get all the items from a which starts with item of b using String.prototype.startsWith()
At last set it as property of the ac and return ac
const a = [
{ id: '1-1-1', name: 'a111' },
{ id: '1-1-2', name: 'a112' },
{ id: '1-2-1', name: 'a121' },
{ id: '1-2-2', name: 'a122' },
{ id: '2-1-1', name: 'a211' },
{ id: '2-1-2', name: 'a212' }
]
const b = ['1-1', '1-2', '2-1']
let res = b.reduce((ac,b) => {
ac[b] = a.filter(x => x.id.startsWith(b));
return ac;
},{})
console.log(res)
As suggested by #Falco is the comments that It would be better to scan over the a once as its large. So here is that version.Actually its better regarding performance
const a = [
{ id: '1-1-1', name: 'a111' },
{ id: '1-1-2', name: 'a112' },
{ id: '1-2-1', name: 'a121' },
{ id: '1-2-2', name: 'a122' },
{ id: '2-1-1', name: 'a211' },
{ id: '2-1-2', name: 'a212' }
]
const b = ['1-1', '1-2', '2-1']
let res = a.reduce((ac,x) => {
let temp = b.find(y => x.id.startsWith(y))
if(!ac[temp]) ac[temp] = [];
ac[temp].push(x);
return ac;
},{})
console.log(res)
Note: startsWith is not supported by I.E. So you can create polyfill using indexOf
if(!String.prototype.startWith){
String.prototype.startsWith = function(str){
return this.indexOf(str) === 0
}
}
Assuming I have the following array in a JSON file:
[
{ id: 1 },
{ name: 'foo' },
{ id: 3, name: 'foo', nick: 'bar' },
{ id: 4, nick: 'next' },
{ nick: 'nextnext' }
]
How to get the object with more properties? In this example I should get the third item: { id: 3, name: 'foo', nick: 'bar' }
If there is another object with 3 properties, I can get two results or the last found, it doesn't matter, my purpose is to know all properties an object can have.
To cope with multiple results, you could use filter.
var data = [
{ id: 1 },
{ name: 'foo' },
{ id: 3, name: 'foo', nick: 'bar' },
{ id: 4, nick: 'next' },
{ nick: 'nextnext' },
{ id: 6, name: 'another 3', nick: '3'}
]
const mx = Math.max(...data.map(m => Object.keys(m).length));
const res = data.filter(f => Object.keys(f).length === mx)
console.log(res);
You can create an array and put values based on key length.
Since you want objects with most keys, you can get the last item.
var data = [
{ id: 1 },
{ name: 'foo' },
{ id: 3, name: 'foo', nick: 'bar' },
{ id: 4, nick: 'next' },
{ nick: 'nextnext' }
];
var res = data.reduce((a, c) => {
const len = Object.keys(c).length;
a[len] = a[len] || [];
a[len].push(c);
return a;
}, []).pop();
console.log(res);
You can use reduce and Object.keys() to return the object which has more length.
Try the following way:
var data = [
{ id: 1 },
{ name: 'foo' },
{ id: 3, name: 'foo', nick: 'bar' },
{ id: 4, nick: 'next' },
{ nick: 'nextnext' }
]
var res = data.reduce((a, c) => {
return Object.keys(a).length > Object.keys(c).length ? a : c;
})
console.log(res);
let biggestObj = {};
for(let el of array){
if(Object.keys(el).length > Object.keys(biggestObj).length){
biggestObj = el;
}
}
This should do the job!