I have a nested array . I want to take values from array and push to new single object.
should read take the _id as object key and category array field should be value for _id
const Ll = [
{
_id: 'milk',
category: [
[
{
name: 'Alfred',
job: 'manager'
},
{
name: 'Mark',
job: 'manager'
}
]
]
},
{
_id: 'grocery',
category: [
[
{
name: 'William',
job: 'manager'
}
]
]
}
]
I want object like so,
const obj = {
milk: [
{
name: 'Alfred',
job: 'manager'
},
{
name: 'Mark',
job: 'manager'
}
],
grocery: [
{
name: 'William',
job: 'manager'
}
]
}
Is possible to do
Thanks!
You could do it using Array.prototype.reduce() method. Traverse the array and group it by _id.
const data = [
{
_id: 'milk',
category: [
[
{
name: 'Alfred',
job: 'manager',
},
],
],
},
{
_id: 'grocery',
category: [
[
{
name: 'William',
job: 'manager',
},
],
],
},
{
_id: 'milk',
category: [
[
{
name: 'Mark',
job: 'manager',
},
],
],
},
];
const ret = data.reduce((prev, c) => {
const p = prev;
const key = c._id;
p[key] = p[key] ?? [];
p[key].push(...c.category.flat());
return p;
}, {});
console.log(ret);
ES6:
const data = [
{
_id: 'milk',
category: [
[
{
name: 'Alfred',
job: 'manager',
},
],
],
},
{
_id: 'grocery',
category: [
[
{
name: 'William',
job: 'manager',
},
],
],
},
{
_id: 'milk',
category: [
[
{
name: 'Mark',
job: 'manager',
},
],
],
},
];
const ret = data.reduce((prev, c) => {
const p = prev;
const key = c._id;
p[key] = p[key] || [];
p[key].push(...c.category.reduce((acc, val) => acc.concat(val), []));
return p;
}, {});
console.log(ret);
Here is solution for you.
const obj = {};
data.forEach(d => {
const categories = d.category.reduce((a, v) => a.concat(v), []);
obj[d._id] = obj[d._id] ? [...obj[d._id], ...categories] : [...categories];
});
console.log(obj);
You could use reduce with empty object accumulated
Technique combined with:
computed property [_id]: category
object destruction (acc, { _id, category }) as well as ({...acc})
const data = [ { _id: "milk", category: [ [ { name: "Alfred", job: "manager", }, { name: "Mark", job: "manager", }, ], ], }, { _id: "grocery", category: [ [ { name: "William", job: "manager", }, ], ], }, ];
const res = data.reduce(
(acc, { _id, category }) => ({ ...acc, [_id]: category.flat() }),
{}
);
console.log(res);
const keys = [...new Set(data.map(item => item._id))];
const newObj = keys.reduce((acc, curr) => {
const value = data.filter(({ _id }) => _id === curr).map(({ category }) => {
return {
...category[0][0]
}
});
return {
...acc,
[curr]: value
}
}, {});
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 have the following array that I'd like to transform into an Object with unique hobbies as the keys
const arr = [
{ name: 'Joe', hobbies: ['skating', 'biking', 'music'] },
{ name: 'Kim', hobbies: ['fishing', 'biking', 'karate'] },
{ name: 'Ben', hobbies: ['surfing'] },
]
I use lodash's handy groupBy function but it groups the multiple array elements into single keys like so
{
'skating,biking,music': [
{ name: 'Joe' }
],
'fishing,biking,karate': [
{ name: 'Kim' }
],
'surfing': [
{ name: 'Ben' }
],
}
What I need is the following output (note the objects are repeated for each of their respective hobbies)
{
biking: [
{ name: 'Joe' },
{ name: 'Kim' }
],
skating: [
{ name: 'Joe' }
],
karate: [
{ name: 'Kim' }
],
surfing: [
{ name: 'Ben' }
],
...
}
Is there a simple way to group this array without looping through each array element, splitting them up and regrouping? Would like to avoid this if there's better utility method out there I'm unaware of
You can iterate each item and each hobbie and then add it to a result object:
const arr = [
{ name: 'Joe', hobbies: ['skating', 'biking', 'music'] },
{ name: 'Kim', hobbies: ['fishing', 'biking', 'karate'] },
{ name: 'Ben', hobbies: ['surfing'] }
]
const result = {};
arr.forEach(item =>
item.hobbies.forEach(hobbie =>
result[hobbie] = (result[hobbie] || []).concat({name: item.name})
)
)
console.log(result);
const arr = [
{ name: 'Joe', hobbies: ['skating', 'biking', 'music'] },
{ name: 'Kim', hobbies: ['fishing', 'biking', 'karate'] },
{ name: 'Ben', hobbies: ['surfing'] }
]
const result = {};
arr.forEach(item =>
item.hobbies.forEach(hobbie =>
result[hobbie] = result[hobbie]?[...result[hobbie],{name: item.name}]: [{name: item.name}]
)
)
console.log(result);
I've renamed arr to people for better understanding.
const people = [
{ name: 'Joe', hobbies: ['skating', 'biking', 'music'] },
{ name: 'Kim', hobbies: ['fishing', 'biking', 'karate'] },
{ name: 'Ben', hobbies: ['surfing'] },
];
function transform(people) {
// get all hobbies and remove duplicates
const hobbies = [... new Set(
people.reduce((hobbies, person) => hobbies.concat(person.hobbies), [])
)];
const res = {};
// take a hobby and use it as key
for (let hobby of hobbies) {
res[hobby] = people
.filter((person) => person.hobbies.includes(hobby))
.map((person) => { return { name: person.name }; });
}
return res;
}
console.log(transform(people));
Given an array of json object like this below, (the json object such as "name2" and "name4" will definitely have only one key-value)
[
{
abc: 123,
id: '18263322',
name: 'name1'
},
{ name: 'name2' },
{
abc: 456,
id: '18421634',
name: 'name3'
},
{ name: 'name4' }
]
How can I subset this so that I have two array of json objects:
[
{
abc: 123,
id: '18263322',
name: 'name1'
},
{
abc: 456,
id: '18421634',
name: 'name3'
}
]
and
[
{ name: 'name2' },
{ name: 'name4' }
]
You can use reduce here
const arr = [
{
abc: 123,
id: "18263322",
name: "name1",
},
{ name: "name2" },
{
abc: 456,
id: "18421634",
name: "name3",
},
{ name: "name4" },
];
const [single, multiple] = arr.reduce((acc, curr) => {
Object.keys(curr).length === 1 ? acc[0].push(curr) : acc[1].push(curr);
return acc;
},[[], []]
);
console.log(single);
console.log(multiple);
You can also do something like
const [single, multiple] = arr.reduce((acc, curr) => {
acc[Object.keys(curr).length === 1 ? 0 : 1].push(curr);
return acc;
},[[], []]);
using filter
const arr = [
{
abc: 123,
id: "18263322",
name: "name1",
},
{ name: "name2" },
{
abc: 456,
id: "18421634",
name: "name3",
},
{ name: "name4" },
];
const single = arr.filter((o) => Object.keys(o).length === 1);
const multiple = arr.filter((o) => Object.keys(o).length !== 1);
console.log(single);
console.log(multiple);
I have array of object like this
const data = [
{
name: "John",
transaction: "10/10/2010",
item: "Bag"
},
{
name: "Steven",
transaction: "31/10/2020",
item: "Shoe"
},
{
name: "John",
transaction: "18/06/2019",
item: "Sock"
}
]
you can see that the name of object in that array has duplicate name but different transaction
and then I want the result like this :
const result = [
{
name: "John",
transactions: [
{
date: "10/10/2010",
item: "Bag"
},
{
date: "18/06/2019",
item: "Sock"
}
]
},
{
name: "Steven",
transactions: [
{
date: "31/10/2020",
item: "Shoe"
}
]
},
]
so the new array recored the new transactions of the same person
the code for this is:
const data = [
{
name: "John",
transaction: "10/10/2010",
item: "Bag"
},
{
name: "Steven",
transaction: "31/10/2020",
item: "Shoe"
},
{
name: "John",
transaction: "18/06/2019",
item: "Sock"
}
]
let Transactions = []
data.forEach(data => {
Transactions.some(t => {
if(t.name === data.name){
t.transactions.push({date:data.transaction,item:data.item})
return;
}
})
Transactions.push({
name:data.name,
transactions:[
{date:data.transaction,item:data.item}
]
})
console.log(Transactions);
})
array.some is better than forEach loop i think.so decided to stick with that.
Please try the following example
const data = [
{
name: "John",
transaction: "10/10/2010",
item: "Bag",
},
{
name: "Steven",
transaction: "31/10/2020",
item: "Shoe",
},
{
name: "John",
transaction: "18/06/2019",
item: "Sock",
},
];
const output = data.reduce((previousValue, { name, transaction, item }) => {
const index = previousValue.findIndex((entry) => entry.name === name);
if (index === -1) {
previousValue = [
...previousValue,
{
name: name,
transactions: [{ date: transaction, item }],
},
];
} else {
previousValue[index].transactions = previousValue[
index
].transactions.concat({
date: transaction,
item,
});
}
return previousValue;
}, []);
console.dir(output, { depth: null, color: true });
See
Array.prototype.reduce()
Array.prototype.concat()
Array.prototype.findIndex()
a simple reduce do that
const data =
[ { name: 'John', transaction: '10/10/2010', item: 'Bag' }
, { name: 'Steven', transaction: '31/10/2020', item: 'Shoe' }
, { name: 'John', transaction: '18/06/2019', item: 'Sock' }
]
const result = data.reduce((a,{name,transaction:date,item})=>
{
let x = a.find(e=>e.name===name)
if (!x)
{
let n = a.push({name, transactions:[]}) -1
x = a[n]
}
x.transactions.push({date,item})
return a
},[])
console.log(result)
.as-console-wrapper { max-height: 100% !important; top: 0; }
shorter version
const result = data.reduce((a,{name,transaction:date,item})=>
{
let x = a.find(e=>e.name===name) || (a[a.push({name, transactions:[]}) -1])
x.transactions.push({date,item})
return a
},[])
You could do that in a functional way to make it readable, below worked solution is using ramdajs
const data = [
{
name: 'John',
transaction: '10/10/2010',
item: 'Bag'
},
{
name: 'Steven',
transaction: '31/10/2020',
item: 'Shoe'
},
{
name: 'John',
transaction: '18/06/2019',
item: 'Sock'
}
]
const result = pipe(
groupBy(obj => obj.name),
mapObjIndexed((groupObjs, groupName) => ({
name: groupName,
transactions: map(
groupObj => ({
date: groupObj.transaction,
item: groupObj.item
}),
groupObjs
)
})),
values
)(data)
console.log(result)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script>
<script>const { groupBy, mapObjIndexed, pipe, map, values } = R</script>
Here is the link to the ramdajs doc
How about using lodash's _.groupBy() function?
const data = [
{
name: "John",
transaction: "10/10/2010",
item: "Bag",
},
{
name: "Steven",
transaction: "31/10/2020",
item: "Shoe",
},
{
name: "John",
transaction: "18/06/2019",
item: "Sock",
}
]
const result = _.groupBy(data, "name")
console.log(result)
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.15/lodash.min.js"></script>
I want to search an nested object by values of property 'name' and the result will keep its all parents.
For example,
const object = [
{
name: 'Mary',
children: [
{
name: 'Jack',
},
{
name: 'Kevin',
children: [
{
name: 'Lisa',
}
]
}
]
},
{
name: 'Gina',
children: [
{
name: 'Jack',
}
]
}
]
If I search 'Mary', it should be return:
[
{
name: 'Mary',
}
]
If I search 'Jack', it should be return:
[
{
name: 'Mary',
children: [
{
name: 'Jack',
}
]
},
{
name: 'Gina',
children: [
{
name: 'Jack',
}
]
}
]
If I search 'Lisa', it should be return:
[
{
name: 'Mary',
children: [
{
name: 'Jack',
children: [
{
name: 'Lisa',
}
]
}
]
}
]
I tried some methods but I could only filter two layer. As below:
return object.filter(data => {
if (data.children) {
return data.name.includes(keyword) || data.children.find(item => item.name.includes(keyword));
}
return data.name.includes(keyword);
})
Could someone point me in the right direction? Thanks!
You could build an object and if nested, check the children and create the parents, if necessary.
function getObjects(array, target) {
return array.reduce((r, { name, children = [] }) => {
if (name === target) {
r.push({ name });
return r;
}
children = getObjects(children, target);
if (children.length) {
r.push({ name, children })
}
return r;
}, []);
}
var data = [{ name: 'Mary', children: [{ name: 'Jack' }, { name: 'Kevin', children: [{ name: 'Lisa' }] }] }, { name: 'Gina', children: [{ name: 'Jack' }] }];
console.log(getObjects(data, 'Mary'));
console.log(getObjects(data, 'Jack'));
console.log(getObjects(data, 'Lisa'));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Here is an example of a depth-first approach:
function searchWithParents(tree, query) {
let results = [];
for (const {name, children} of tree) {
if (name === query) {
results.push({name});
}
if (children) {
const subtreeResults = searchWithParents(children, query);
const mappedResults = subtreeResults.map(child => ({name, children: [child]}))
results = results.concat(mappedResults);
}
}
return results;
}
console.log(searchWithParents(object, 'Mary'));
console.log(searchWithParents(object, 'Jack'));
console.log(searchWithParents(object, 'Lisa'));