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>
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 an two arrays of objects. My goal is to replace an object from the second array into the first one based upon 'id'. I have a working solution, but would like to extend it by adding the object to the first array if a value isnt found. Please advice.
function mergeById(arr) {
return {
with: function(arr2) {
return _.map(arr, item => {
return _.find(arr2, obj => obj.id === item.id) || item
})
}
}
}
var result = mergeById([{
id: '124',
name: 'qqq'
},
{
id: '589',
name: 'www'
},
{
id: '567',
name: 'rrr'
}
])
.with([{
id: '124',
name: 'ttt'
}, {
id: '45',
name: 'yyy'
}])
console.log(result);
/**
[
{
"id": "124",
"name": "ttt"
},
{
"id": "589",
"name": "www"
},
{
"id": "567",
"name": "rrr"
},
{
id: '45',
name: 'yyy'
}
]
**/
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.20/lodash.min.js"></script>
Please advice.
You need to filter the second array and add the values who have no common id.
function mergeById(arr) {
return {
with: function(arr2) {
return [
..._.map(arr, item => _.find(arr2, obj => obj.id === item.id) || item),
..._.filter(arr2, item => !_.some(arr, obj => obj.id === item.id))
];
}
}
}
var result = mergeById([{ id: '124', name: 'qqq' }, { id: '589', name: 'www' }, { id: '567', name: 'rrr' } ])
.with([{ id: '124', name: 'ttt' }, { id: '45', name: 'yyy' }]);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.20/lodash.min.js"></script>
A shorter approach with a Map and single loops for every array.
function mergeById(array) {
const
add2map = (m, o) => m.set(o.id, o),
map = array.reduce(add2map, new Map);
return {
with: function(array2) {
return Array.from(array2
.reduce(add2map, map)
.values()
);
}
}
}
var result = mergeById([{ id: '124', name: 'qqq' }, { id: '589', name: 'www' }, { id: '567', name: 'rrr' } ])
.with([{ id: '124', name: 'ttt' }, { id: '45', name: 'yyy' }]);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Use _.differenceBy(arr2, arr, 'id') to find all items that appear in arr2 that doesn't have a counterpart in arr by id, and concat them to the results of the _.map() action.
Note: instead using _.find() (O(n)) on each iteration, iterate arr2 once with _.keyBy() (O(n)) to create a dictionary { [id]: item }, and then get the items in O(1).
const mergeById = arr => ({
with(arr2) {
const arr2Dict = _.keyBy(arr2, 'id')
return _.map(arr, item => arr2Dict[item.id] || item)
.concat(_.differenceBy(arr2, arr, 'id'))
}
})
const result = mergeById([{ id: '124', name: 'qqq' }, { id: '589', name: 'www' }, { id: '567', name: 'rrr' } ])
.with([{ id: '124', name: 'ttt' }, { id: '45', name: 'yyy' }])
console.log(result)
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.20/lodash.min.js"></script>
You can replace/add in a single loop by concating both arrays, reducing to a Map, and just adding the items by id to the Map:
const mergeById = arr => ({
with(arr2) {
return Array.from(
[...arr, ...arr2]
.reduce((r, o) => r.set(o.id, o), new Map)
.values()
)
}
})
const result = mergeById([{ id: '124', name: 'qqq' }, { id: '589', name: 'www' }, { id: '567', name: 'rrr' } ])
.with([{ id: '124', name: 'ttt' }, { id: '45', name: 'yyy' }])
console.log(result)
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.20/lodash.min.js"></script>
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
}
}, {});
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
}
}
I would like to create an array of all "department" from the "users" array without duplicate in ES6.
I've tried with forEach, reduce, filter, without success...
Users array:
let users = [{
firstname: 'test',
department: {
id: 1,
name: 'hello'
}
},
{
firstname: 'test2',
department: {
id: 2,
name: 'hello2'
}
},
{
firstname: 'test2',
department: {
id: 1,
name: 'hello'
}
}
]
Result expected:
// Expected
departments = [{
id: 1,
name: 'hello'
},
{
id: 2,
name: 'hello2'
}
] */
My own experiment:
let departments = []
users.forEach(user => {
console.log('-------------------')
console.log(departments)
console.log(user)
console.log(user.department)
console.log(departments.includes(user.department))
if (!departments.includes(user.department)) {
departments.push(user.department)
}
console.log(departments)
})
console.log(departments)
Thanks for your help!
Problem:
Your problem is that you are checking for departments with Array#includes() which is rather used with primitives such as Number and string and doesn't compare objects, try not to use it as it's not compatible with IE also.
Solution:
You can do it using Array#map() and Array#filter() methods:
var deps = users.map(u => u.department);
let results = deps.filter((item, pos) => {
return deps.map(v => v.id).indexOf(item.id) == pos;
});
First map the items to keep only the department object.
Then filter the departments to exclude the ones that has the same id.
Demo:
This is a working demo:
let users = [{
firstname: 'test',
department: {
id: 1,
name: 'hello'
}
},
{
firstname: 'test2',
department: {
id: 2,
name: 'hello2'
}
},
{
firstname: 'test2',
department: {
id: 1,
name: 'hello'
}
}
];
var deps = users.map(u => u.department);
let results = deps.filter((item, pos) => {
return deps.map(v => v.id).indexOf(item.id) == pos;
});
console.log(results);
Just map to the departments, then filter out based on the id:
const ids = new Set;
const result = users
.map(user => user.department)
.filter(({ id }) => !ids.has(id) && ids.add(id));
(This is O(n) as Set lookup / insertion is O(1))
You can use Array.reduce() for that:
let users = [{
firstname: 'test',
department: {
id: 1,
name: 'hello'
}
},
{
firstname: 'test2',
department: {
id: 2,
name: 'hello2'
}
},
{
firstname: 'test2',
department: {
id: 1,
name: 'hello'
}
}
];
let departments = users.reduce((acc, obj)=>{
let exist = acc.find(({id}) => id === obj.department.id);
if(!exist){
acc.push({id:obj.department.id, name: obj.department.name});
}
return acc;
}, []);
console.log(departments);