I've been trying to transform some data using lodash without success. I am really new to javascript and lodash. How can I get the expected result?
I've used mapValues and chain, but I didn't achieve anything good.
const data = {
"north": [
{
"2018-07-01": {
"date": "2018-07-01",
"name": "david",
"age": 11
},
"2018-07-02": {
"date": "2018-07-02",
"name": "damo",
"age": 16
},
"2018-07-03": {
"date": "2018-07-03",
"name": "dani",
"age": 12
}
}
],
"south": [
{
"2018-07-01": [
{
"fruit": "banana",
"date": "2018-07-01",
"name": "miller",
"age": 11
},
{
"fruit": "mango",
"date": "2018-07-01",
"name": "mano",
"age": 11
},
{
"fruit": "avocado",
"date": "2018-07-01",
"name": "karl",
"age": 14
}
],
"2018-07-02": [
{
"fruit": "pineaplle",
"date": "2018-07-02",
"name": "gautier",
"age": 12
},
{
"fruit": "apple",
"date": "2018-07-02",
"name": "gauteng",
"age": 9
},
{
"fruit": "watermelon",
"date": "2018-07-02",
"name": "garzier",
"age": 12
}
]
}
]
};
Below is the expected result. I am trying to remove the dates which are outside the objects and arrays.
const expectedData = {
"north": [
{
"date": "2018-07-01",
"name": "david",
"age": 11
},
{
"date": "2018-07-02",
"name": "damo",
"age": 16
},
{
"date": "2018-07-03",
"name": "dani",
"age": 12
}
],
"south": [
{
"fruit": "banana",
"date": "2018-07-01",
"name": "miller",
"age": 11
},
{
"fruit": "mango",
"date": "2018-07-01",
"name": "mano",
"age": 11
},
{
"fruit": "avocado",
"date": "2018-07-01",
"name": "karl",
"age": 14
},
{
"fruit": "pineaplle",
"date": "2018-07-02",
"name": "gautier",
"age": 12
},
{
"fruit": "apple",
"date": "2018-07-02",
"name": "gauteng",
"age": 9
},
{
"fruit": "watermelon",
"date": "2018-07-02",
"name": "garzier",
"age": 12
}
]
};
You don't really need lodash for this. You can look at each key in your data and the just pull the values from each element of the array and concat it into a new array.
const data = {"north": [{"2018-07-01": {"date": "2018-07-01","name": "david","age": 11},"2018-07-02": {"date": "2018-07-02","name": "damo","age": 16},"2018-07-03": {"date": "2018-07-03","name": "dani","age": 12}}],"south": [{"2018-07-01": [{"fruit": "banana","date": "2018-07-01","name": "miller","age": 11},{"fruit": "mango","date": "2018-07-01","name": "mano","age": 11},{"fruit": "avocado","date": "2018-07-01","name": "karl","age": 14}],"2018-07-02": [{"fruit": "pineaplle","date": "2018-07-02","name": "gautier","age": 12},{"fruit": "apple","date": "2018-07-02","name": "gauteng","age": 9},{"fruit": "watermelon","date": "2018-07-02","name": "garzier","age": 12}]}]};
Object.keys(data).forEach(k => {
data[k] = data[k].reduce((a, c) => a.concat(...Object.values(c)), [])
})
console.log(data)
This starts with each key in your original object north and south. And for each one replaces the array with the accumulated values of each object in that array ignoring the keys.
alternatively you could just do this
const expectedData = {
north: Object.values(data.north[0]),
south: Object.values(data.south[0])
}
You can do any lodash quick operations, in pure JS way. But, since you tagged lodash
here is the version:
_.mapValues(data, v => _.flatMapDeep(v, _.values))
var data = {
"north": [
{
"2018-07-01": {
"date": "2018-07-01",
"name": "david",
"age": 11
},
"2018-07-02": {
"date": "2018-07-02",
"name": "damo",
"age": 16
},
"2018-07-03": {
"date": "2018-07-03",
"name": "dani",
"age": 12
}
}
],
"south": [
{
"2018-07-01": [
{
"fruit": "banana",
"date": "2018-07-01",
"name": "miller",
"age": 11
},
{
"fruit": "mango",
"date": "2018-07-01",
"name": "mano",
"age": 11
},
{
"fruit": "avocado",
"date": "2018-07-01",
"name": "karl",
"age": 14
}
],
"2018-07-02": [
{
"fruit": "pineaplle",
"date": "2018-07-02",
"name": "gautier",
"age": 12
},
{
"fruit": "apple",
"date": "2018-07-02",
"name": "gauteng",
"age": 9
},
{
"fruit": "watermelon",
"date": "2018-07-02",
"name": "garzier",
"age": 12
}
]
}
]
};
var expectedData = _.mapValues(data, v => _.flatMapDeep(v, _.values));
console.log(expectedData);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
Related
I have JSON like below, I need to filter out workers having the age less than 25.
var employee = {
"value": [
{
"position": "Seniro Developer",
"description": "Developemwnt",
"workers": [
{
"name": "Kumar",
"age": 22
},
{
"name": "aravinth",
"age": 29
},
{
"name": "sathish",
"age": 35
}
]
},
{
"position": "Tester",
"description": "testing",
"workers": [
{
"name": "vinth",
"age": 18
},
{
"name": "rahul",
"age": 45
},
{
"name": "sathish",
"age": 12
}
]
}
]
}
I have tried to use the below code, but it returns all the value inside the workers array, but my expectation is it should return only the employee having than 25.
If I use Map function it is affecting the employee Object also.
var filteredResult = employee.filter(e => e.workers.some(w => w.age < 25))
Expected Result:
{
"value": [
{
"position": "Seniro Developer",
"description": "Developemwnt",
"workers": [
{
"name": "Kumar",
"age": 22
}
]
},
{
"position": "Tester",
"description": "testing",
"workers": [
{
"name": "vinth",
"age": 18
},
{
"name": "sathish",
"age": 12
}
]
}
]
}
You can do it with a map and a filter, to avoid to modify the original array, you can use Object.asign
var employee = {
"value": [{
"position": "Seniro Developer",
"description": "Developemwnt",
"workers": [{
"name": "Kumar",
"age": 22
},
{
"name": "aravinth",
"age": 29
},
{
"name": "sathish",
"age": 35
}
]
},
{
"position": "Tester",
"description": "testing",
"workers": [{
"name": "vinth",
"age": 18
},
{
"name": "rahul",
"age": 45
},
{
"name": "sathish",
"age": 12
}
]
}
]
}
var filteredResult = employee.value.map(e => {
let filter = e.workers.filter(w => w.age < 25)
return Object.assign({}, e, {workers: filter})
})
console.log('original', employee)
console.log('result', filteredResult)
You could reduce the array and check if the filtered workers have some elements then push a new object with changed workers to the result set.
var employee = { value: [{ position: "Seniro Developer", description: "Developemwnt", workers: [{ name: "Kumar", age: 22 }, { name: "aravinth", age: 29 }, { name: "sathish", age: 35 }] }, { position: "Tester", description: "testing", workers: [{ name: "vinth", age: 18 }, { name: "rahul", age: 45 }, { name: "sathish", age: 12 }] }] },
value = employee.value.reduce((r, o) => {
const workers = o.workers.filter(({ age }) => age < 25);
if (workers.length) r.push({ ...o, workers });
return r;
}, []),
result = { value };
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can also try this:
var employee = { "value": [ { "position": "Seniro Developer", "description": "Developemwnt", "workers": [ { "name": "Kumar", "age": 22 }, { "name": "aravinth", "age": 29 }, { "name": "sathish", "age": 35 } ] }, { "position": "Tester", "description": "testing", "workers": [ { "name": "vinth", "age": 18 }, { "name": "rahul", "age": 45 }, { "name": "sathish", "age": 12 } ] } ]}
result = employee.value.map(({workers, ...rest})=>({...rest, workers:[...workers.filter(k=>k.age<25)]}));
console.log(result);
Use map and while creating the workers key in return object use filter to get employee with age less than 25. map will create an array
var employee = {
"value": [{
"position": "Seniro Developer",
"description": "Developemwnt",
"workers": [{
"name": "Kumar",
"age": 22
},
{
"name": "aravinth",
"age": 29
},
{
"name": "sathish",
"age": 35
}
]
},
{
"position": "Tester",
"description": "testing",
"workers": [{
"name": "vinth",
"age": 18
},
{
"name": "rahul",
"age": 45
},
{
"name": "sathish",
"age": 12
}
]
}
]
}
let filteredEmployee = employee.value.map((item) => {
return {
"position": item.position,
"description": item.description,
"workers": item.workers.filter(elem => elem.age < 25)
}
});
let newObject = Object.assign({}, {
value: filteredEmployee
});
console.log(newObject)
You can use map method with ... rest syntax:
employee.value.map(({workers, ...rest}) => ({...rest,
workers: workers.filter(w => w.age < 25)}));
An example:
let employee = {
"value": [
{
"position": "Seniro Developer",
"description": "Developemwnt",
"workers": [
{
"name": "Kumar",
"age": 22
},
{
"name": "aravinth",
"age": 29
},
{
"name": "sathish",
"age": 35
}
]
},
{
"position": "Tester",
"description": "testing",
"workers": [
{
"name": "vinth",
"age": 18
},
{
"name": "rahul",
"age": 45
},
{
"name": "sathish",
"age": 12
}
]
}
]
}
const result = employee.value.map(({workers, ...rest}) => ({...rest, workers: workers.filter(w => w.age < 25)}));
console.log(result);
I've got the following JSON:
var obj =
{
"workers": [
{
"TimeStamp": "2020-03-13T10:08",
"Status": "status1",
"Name": "one",
"Number": 19.9
},
{
"TimeStamp": "2019-07-19T06:01",
"Status": "status2",
"Name": "one",
"Number": 9
},
{
"TimeStamp": "2020-04-22T05:10",
"Status": "status2",
"Name": "one",
"Number": 10.1
},
{
"TimeStamp": "2019-07-21T23:53",
"Status": "status2",
"Name": "two",
"Number": 16.3
},
{
"TimeStamp": "2019-11-21T05:14",
"Status": "status1",
"Name": "three",
"Number": 122.54
},
...
]
};
As you see there's just 2 different status possible: "status1" and "status2".
Names should be filtered to be shown just once, but combine the two different status.
The respective status should include the "TimeStamp" and "Number" in an array.
In the end it should look like this:
{
"workers": [
{
"Name":"one",
"status1": [
{
"TimeStamp":"2020-03-13T10:08",
"Number": 19.9
}
],
"status2": [
{
"TimeStamp":"2019-07-19T06:01",
"Number": 9
},
{
"TimeStamp": "2020-04-22T05:10",
"Number": 10.1
},
]
},
{
"Name":"two",
"status1": [],
"status2": [
{
"TimeStamp":"2019-07-21T23:53",
"Number": 16.3
}
]
},
{
"Name":"three",
"status1": [
{
"TimeStamp":"2019-11-21T05:14",
"Number": 122.54
}
],
"status2": []
}
]
}
I tried out the following so far:
var writeObj = { 'workers': [] };
for (var i = 0; i < obj.workers.length; i++) {
if(!Object.values(writeObj.workers).includes(obj.workers[i].Name)) {
writeObj['workers'].push({ Name: obj.workers[i].Name, 'status1': [], 'status2': [] });
for (var j = 0; j < obj.workers.length; j++) {
if (obj.workers[j].Name === obj.workers[i].Name && obj.workers[j].Status === 'status1') {
writeObj['workers'][i]['status1'].push({ TimeStamp: obj.workers[j].TimeStamp, Number: obj.workers[j].Number });
} else if (obj.workers[j].Name === obj.workers[i].Name && obj.workers[j].Status === 'status2') {
writeObj['workers'][i]['status2'].push({ TimeStamp: obj.workers[j].TimeStamp, Number: obj.workers[j].Number });
}
}
}
}
I'm stuck and can't see the mistake...
Thanks for any help!
You can aggregate your data using array.reduce:
var obj =
{
"workers": [
{
"TimeStamp": "2020-03-13T10:08",
"Status": "status1",
"Name": "one",
"Number": 19.9
},
{
"TimeStamp": "2019-07-19T06:01",
"Status": "status2",
"Name": "one",
"Number": 9
},
{
"TimeStamp": "2020-04-22T05:10",
"Status": "status2",
"Name": "one",
"Number": 10.1
},
{
"TimeStamp": "2019-07-21T23:53",
"Status": "status2",
"Name": "two",
"Number": 16.3
},
{
"TimeStamp": "2019-11-21T05:14",
"Status": "status1",
"Name": "three",
"Number": 122.54
}
]
};
let output = obj.workers.reduce((acc,cur) => {
let {Name, Status, ...rest} = cur;
let match = acc.find(x => x.Name === Name);
if(!match){
match = { Name: Name };
acc.push(match);
}
if(!match[Status]){
match[Status] = [];
}
match[Status].push(rest);
return acc;
}, []);
console.log({workers: output});
You can use Array#reduce. Group by the Name key according to your format, then take the grouped values as the result. Time complexity is O(n).
var obj = { "workers": [ { "TimeStamp": "2020-03-13T10:08", "Status": "status1", "Name": "one", "Number": 19.9 }, { "TimeStamp": "2019-07-19T06:01", "Status": "status2", "Name": "one", "Number": 9 }, { "TimeStamp": "2020-04-22T05:10", "Status": "status2", "Name": "one", "Number": 10.1 }, { "TimeStamp": "2019-07-21T23:53", "Status": "status2", "Name": "two", "Number": 16.3 }, { "TimeStamp": "2019-11-21T05:14", "Status": "status1", "Name": "three", "Number": 122.54 }, ] };
const grouped = Object.values(obj.workers.reduce((a, e) => {
if (!a[e.Name]) {
a[e.Name] = {Name: e.Name, status1: [], status2: []};
}
a[e.Name][e.Status].push({TimeStamp: e.TimeStamp, Number: e.Number});
return a;
}, {}));
console.log(grouped);
Following code gets the result below in a way that multiple iterations required. I wonder what would be the way to make it happen in a single or less iterations. Thanks in advance.
var input = [{
"ActiveMembers": [{
"Id": 101,
"Name": "alpha"
}, {
"Id": 102,
"Name": "bravo"
}],
"Contents": [{
"Id": 2001,
"RowId": "517",
"Time": "19 Jan 2017",
"ViewCount": 1124
}, {
"Id": 2002,
"RowId": "518",
"Time": "Today, 07:02 PM",
"ViewCount": 62
}],
"TotalUsers": 3,
"UsersDetails": "2 members, 1 anonymous users"
}, {
"ActiveMembers": [{
"Id": 101,
"Name": "alpha"
}, {
"Id": 103,
"Name": "charlie"
}, {
"Id": 104,
"Name": "delta"
}, {
"Id": 105,
"Name": "bravo"
}],
"Contents": [{
"Id": 2002,
"RowId": "519",
"Time": "27 Jun 2017",
"ViewCount": 4833
}, {
"Id": 2041,
"RowId": "525",
"Time": "17 Feb 2015",
"ViewCount": 24491
}],
"TotalUsers": 23,
"UsersDetails": "4 members, 19 anonymous users"
}];
var contents = Array.prototype.concat.apply([], input.map(i => i.Contents));
var activeMembers = _.uniqBy(Array.prototype.concat.apply([], input.map(i => i.ActiveMembers)), (i) => i.Id);
var totalUsers = number = _.sumBy(input, (i) => i.TotalUsers);
var userDetails = string = input.map(i => i.UsersDetails).join(' ; ');
const result = new Object();
result.Contents = contents;
result.ActiveMembers = activeMembers;
result.TotalUsers = totalUsers;
result.UserDetails = userDetails;
console.log(JSON.stringify(result));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
Result
{
"ActiveMembers": [
{
"Id": 101,
"Name": "alpha"
},
{
"Id": 102,
"Name": "bravo"
},
{
"Id": 103,
"Name": "charlie"
},
{
"Id": 104,
"Name": "delta"
},
{
"Id": 105,
"Name": "bravo"
}
],
"Contents": [
{
"Id": 2001,
"RowId": "517",
"Time": "19 Jan 2017",
"ViewCount": 1124
},
{
"Id": 2002,
"RowId": "518",
"Time": "Today, 07:02 PM",
"ViewCount": 62
},
{
"Id": 2002,
"RowId": "519",
"Time": "27 Jun 2017",
"ViewCount": 4833
},
{
"Id": 2041,
"RowId": "525",
"Time": "17 Feb 2015",
"ViewCount": 24491
}
],
"TotalUsers": 26,
"UsersDetails": "2 members, 1 anonymous users;4 members, 19 anonymous users"
}
Aggregate the data in a single iteration.
let ActiveMembers = [];
let Contents = [];
let TotalUsers = 0;
let UserDetails = [];
input.forEach((item) => {
ActiveMembers = ActiveMembers.concat(item.ActiveMembers);
Contents = Contents.concat(item.Contents);
TotalUsers += item.TotalUsers;
UserDetails.push(item.UsersDetails);
});
const result = {
ActiveMembers: _.uniqBy(ActiveMembers, "Id"),
Contents: Contents,
TotalUsers: TotalUsers,
UserDetails: UserDetails.join(";")
};
console.log(JSON.stringify(result));
I have two collections A and B. ( A,B have the exactly same structure, but different nodes values)
now I'd like to add A into B, with exactly the order: A B and without merging or changing any
nodes inside them. ( just like a joint action) { A } + {B}
I've read the documentation for underscore but couldn't find a proper function which gets this job done.
any idea?
==========update with example ========Sample is simplified from a larger structure, if there are errors please let me know=========
var collection1 = [{
"date": "29 January 2014",
"items": [{
"name": "Jack",
"score": 90,
"title": "programmer"
}, {
"name": "TOM",
"score": 52,
"title": "designer"
}]
}, {
"date": "28 January 2014",
"items": [{
"name": "Jim",
"score": 30,
"title": "driver"
}, {
"name": "William",
"score": 52,
"title": "worker"
}]
}]
var collect2 = [{
"date": "26 January 2014",
"items": [{
"name": "Marry",
"score": 92,
"title": "teacher"
}, {
"name": "TOM",
"score": 52,
"title": "designer"
}]
}]
========expected output==============
[{
"date": "29 January 2014",
"items": [{
"name": "Jack",
"score": 90,
"title": "programmer"
}, {
"name": "TOM",
"score": 52,
"title": "designer"
}]
}, {
"date": "28 January 2014",
"items": [{
"name": "Jim",
"score": 30,
"title": "driver"
}, {
"name": "William",
"score": 52,
"title": "worker"
}]
}, {
"date": "26 January 2014",
"items": [{
"name": "Marry",
"score": 92,
"title": "teacher"
}, {
"name": "TOM",
"score": 52,
"title": "designer"
}]
}]
I think what you are looking for is simply Array.concat
var foo = ['a','b','c'];
var bar = ['d','e','f'];
var all = foo.concat(bar); // ['a','b','c','d','e','f'];
Use Underscore#extend as : _.extend(collection1, collection2);
DEMO
col1 = { aa: 'aa', cc: 'cc' }; col2 = { bb: 'bb', dd: 'dd' };
_.extend(col1, col2)
console.log(col1);
# Prints Object {aa: "aa", cc: "cc", bb: "bb", dd: "dd"}
I've never tried map/reduce.
How would I get the oldest of each type of animal?
My data is like this:
[
{
"cateory": "animal",
"type": "cat",
"age": 4,
"id": "a"
},
{
"cateory": "animal",
"type": "bird",
"age": 3,
"id": "b"
},
{
"cateory": "animal",
"type": "cat",
"age": 7
"id": "c"
},
{
"cateory": "animal",
"type": "bird",
"age": 4,
"id": "d"
},
{
"cateory": "animal",
"type": "cat",
"age": 8,
"id": "e"
},
{
"cateory": "company",
"type": "Internet",
"age": 5,
"id": "Facebook"
}
]
I'm using node-mongodb-native. Thanks!
Your map function should look something like this:
map = function() {
emit({type: this.type}, {age: this.age});
}
And the reduce function:
reduce = function(key, values) {
maxAge = 0;
values.forEach(function(v) {
if (maxAge < v['age']) {
maxAge = v['age'];
}
});
return {age: maxAge};
}
It's pretty simple:
collection.find({type : 'animal'}).sort({animal: -1}).limit(1);