Filter nested JSON - javascript

I have a JSON object structured like this:
var x = {
zy5EwY0caL3rPk4PXyX3: { name: 'Bob', completedDate: '2019-03-27T01:56:27.589Z'},
zy4HsKbHYZKtZNBMHdxu: { name: 'Tom', completedDate: '2019-04-10T01:56:27.589Z'},
zy0VPwMY51ksZaTFFIxL: { name: 'Jim', completedDate: '2019-05-01T01:56:27.589Z'},
zw6Xmv5PiNE4dmC19q2p: { name: 'Joe', completedDate: '2019-05-02T01:56:27.589Z'}
}
I want to run some javascript in a node script to filter and return only the objects where the completed data is within the last 10 days and return the following.
var filteredData = {
zy0VPwMY51ksZaTFFIxL: { name: 'Jim', completedDate: '2019-05-01T01:56:27.589Z'},
zw6Xmv5PiNE4dmC19q2p: { name: 'Joe', completedDate: '2019-05-02T01:56:27.589Z'}
}
I'm running into issues because the data is nested. I've tried using the .where function in underscore but I don't know the syntax to have it examine the nested elements.
Is there a simple way to get what I want?

Because your source data is not an array, you can't use Array.filter.
So what you could do is use Object.entries to iterate and then place into another object.
Below is a simple example.
const x = {
zy5EwY0caL3rPk4PXyX3: { name: 'Bob', completedDate: '2019-03-27T01:56:27.589Z'},
zy4HsKbHYZKtZNBMHdxu: { name: 'Tom', completedDate: '2019-04-10T01:56:27.589Z'},
zy0VPwMY51ksZaTFFIxL: { name: 'Jim', completedDate: '2019-05-01T01:56:27.589Z'},
zw6Xmv5PiNE4dmC19q2p: { name: 'Joe', completedDate: '2019-05-02T01:56:27.589Z'}
};
const day = 1000 * 60 * 60 * 24;
//const now = Date.now();
//let's set a date time, so snippet will continue to work
//in the future, but Date.now() would be use
//for current time
const now = new Date("2019-05-08T14:29:27.589Z");
const ret = {};
for (const [k, v] of Object.entries(x)) {
const tmc = new Date(v.completedDate);
const diff = Math.abs(tmc.getTime() - now);
if (diff < 10 * day) ret[k] = v;
}
console.log(ret);

Related

Comparing two arrays and fill in missing values

I want to compare the dates from 2 arrays and push the name from the matching date inside a new array. And then push '0' for missing dates.
This is what I've tried
var data = [{
name: 'Amy',
date: '2020-01-01'
}, {
name: 'John',
date: '2020-01-02'
}, {
name: 'Sara',
date: '2020-01-04'
}];
var fulldate = ['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-04', '2020-01-05'];
var newData = [];
var len = data.length;
for (var i = 0; i < len; i++) {
if (data[i].date == fulldate[i]) {
newData.push(data[i].name);
} else if (data[i].date != fulldate[i]) {
newData.push("0")
}
}
console.log(newData);
The problem is that it stops after encountering the unmatched date:
Amy,John,0
This is what I need
Amy, John, 0, Sara, 0
The best approach is always to use map and filter. Use map function on the fulldate The obj filters out the object (if present) with same date, as of current el value. I have used ternary operator in the return statement which returns name if object is present, and 0 otherwise.
var data = [{
name: 'Amy',
date: '2020-01-01'
}, {
name: 'John',
date: '2020-01-02'
}, {
name: 'Sara',
date: '2020-01-04'
}];
var fulldate = ['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-04', '2020-01-05'];
var result = fulldate.map((el) => {
let obj = data.filter(item => (item.date == el))
return (obj[0]) ? obj[0].name : 0;
})
console.log(result);
On your code, you have used for-loop based on data variable length but the result has the same length with fulldate so it will be looped based on fulldate variable length.
Inside loop, you have compared fulldate[i] == data[i].date so compared only same index. Inside the loop, you need to use another loop to find the index of the matched date.
Instead of using for loop, you can simply do it using Array.prototype.map function. (Inside map function, using Array.prototype.findIndex, you can find the matched date index.)
var data = [{
name: 'Amy',
date: '2020-01-01'
}, {
name: 'John',
date: '2020-01-02'
}, {
name: 'Sara',
date: '2020-01-04'
}];
var fulldate = ['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-04', '2020-01-05'];
const result = fulldate.map((date) => {
const existed = data.findIndex(item => item.date === date);
return existed >= 0 ? data[existed].name : 0
});
console.log(result);
var data = [{
name: 'Amy',
date: '2020-01-01'
}, {
name: 'John',
date: '2020-01-02'
}, {
name: 'Sara',
date: '2020-01-04'
}];
var fulldate = [
'2020-01-01',
'2020-01-02',
'2020-01-03',
'2020-01-04',
'2020-01-05'
];
var newData = [];
for(var i = 0; i < fulldate.length; i++) {
var found = false;
for(var k in data) {
if(data[k].date == fulldate[i]) {
newData.push(data[k].name);
found = true;
break;
}
}
if(!found)
newData.push("0");
}
console.log(newData);
Suppose the arrays are ordered by date already, use a variable dataIdx to iterate data and Array.map() to simplify the for loop.
var data = [{name:'Amy', date:'2020-01-01'}, {name:'John', date:'2020-01-02'}, {name:'Sara', date:'2020-01-04'}];
var fulldate = ['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-04', '2020-01-05'];
var dataIdx = 0;
var newData = fulldate.map(date => data[dataIdx] && data[dataIdx].date == date ? data[dataIdx++].name : '0');
console.log(newData);
for variable declaration, use 'let' or 'const' instead of var. reason: var vs let vs const
And, the result you are looking for can be done using map & find js func.
const fulldate = ["2020-01-01", "2020-01-02", "2020-01-03", "2020-01-04", "2020-01-05"];
const data = [
{name: "Amy", date: "2020-01-01"},
{name: "John", date: "2020-01-02"},
{name: "Sara", date: "2020-01-04"}
];
const result = fulldate.map(date => {
const matchingDate = data.find(nameDateObj => nameDateObj['date'] === date);
return matchingDate ? matchingDate['name'] : 0;
});
console.log(result)
fyi: this can also be done using findIndex instead of find too.
fulldate.map(date => {
const matchingDateIndex = data.findIndex(nameDateObj => nameDateObj['date'] === date);
return matchingDateIndex > -1 ? data[matchingDateIndex]['name'] : 0;
});
For shortest solution you should combine map and find. Here is a oneliner:
const data = [{
name: 'Amy',
date: '2020-01-01'
}, {
name: 'John',
date: '2020-01-02'
}, {
name: 'Sara',
date: '2020-01-04'
}];
const fulldate = ['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-04', '2020-01-05'];
const result = fulldate.map(x => (data.find(y => y.date === x) || {name: 0}).name)
console.log(result)

Es6 - Match two different object keys with different name

I am working on two API's that I don't have permission to change their field names (Sample below)
const api1 = {
studentId: 'abc',
studentName: 'John Doe',
studentAge: 19,
course: 'Engineering',
}
const api2 = {
id,
personalInfo: {
name,
age
},
course:
}
Basically I will need to transfer the data from api2 to api 1 so what I did is:
const payload = {}
payload["studentId"] = api2.id,
payload["studentName"] = api2.personalInfo.name,
payload["studentAge"] = api2.personalInfo.age,
payload["course"] = api2.course,
Is there a way how I can do this dynamically ?
You could take a wrapper object which wraps all wanted properties of the given API to a new API.
This approach takes an object which maps the given properties to new format/names of the wanted structure by getting the entries of the object and iterating these until a non object is found and then it assigns the value to a new object. If an object is found it take this object and gets the values from the nested object.
If nested target properties are supplied, like
{ studentName: 'personalInfo.name' }
(for example for creating API2 from API1) you need to split the value and create an object with a nested structure, like
const
path = value.split('.'),
last = path.pop();
path.reduce((o, k) => o[k] ??= {}, target)[last] = source[key];
in the else part.
const
assign = (source, wrapper, target = {}) => {
Object.entries(wrapper).forEach(([key, value]) => {
if (value && typeof value === 'object') assign(source[key], value, target);
else target[value] = source[key];
});
return target;
},
api1 = { studentId: 'abc', studentName: 'John Doe', studentAge: 19, course: 'Engineering' },
api2 = { id: 'someId', personalInfo: { name: 'someName', age: 'someAge' }, course: 'someCourse' },
wrapper = { id: 'studentId', personalInfo: { name: 'studentName', age: 'studentAge' }, course: 'course' },
payload = assign(api2, wrapper);
console.log(payload);

Javascript how to push a new element from an initial array to each array inside an array of arrays at the same index?

I want to add new property to an array from another array using indexes of both.
For example, lets say I have the following array:
initArray = [
{ name: 'John', family: 'Doe'},
{ name: 'Joanna', family: 'Doe'}
];
And the array that I want to take from it and push it into initArray:
genderArray = [
{ gender: 'Male' },
{ gender: 'Female' }
];
What I need is a final array that be like:
initArray = [
{ name: 'John', family: 'Doe', gender: 'Male'},
{ name: 'Joanna', family: 'Doe', gender: 'Female'}
];
I tried the following:
ngOnInit() {
Object.keys(this.genderArray).forEach((idx) => {
console.log(this.initArray)
this.initArray[idx].push(this.genderArray)
});
console.log(this.initArray)
}
But it returned an error of:
Error: _this.initArray[idx].push is not a function
Here is a stackblitz.
First, you should not push to initArray: you do not add elements to initArray, but instead add a property to each of its elements.
One possible way to do that:
Object.assign(this.initArray[idx], this.genderArray[idx]);
... or, if for some reason you want to create a completely new array of new 'enriched' objects, instead of modifying objects directly:
this.initArray[idx] = {...this.initArray[idx], ...this.genderArray[idx]};
Second, do not use Object.keys() to go through an array: use either forEach() if you do all the transformations inside the function or map, if you return a new element and want to have a new array in the end.
So your function might look like that:
ngOnInit() {
this.genderArray.forEach((element, index) => {
Object.assign(this.initArray[index], element);
});
console.log(this.initArray)
}
... or ...
ngOnInit() {
this.initArray = this.initArray.map((element, index) => {
{ ...element, ...this.genderArray[index] }
});
console.log(this.initArray)
}
Personally, I always find code 'x = x.map(transformer())` highly suspicious (if one reassign anyway, why not use forEach?), but it's up to implementation style choices.
If there's more than property in the elements of an array to be 'mixed', but you only want to take one, you can either go direct:
Object.assign(this.initArray[idx], { gender: this.genderArray[idx].gender });
... use destructuring in assignment:
const { gender } = this.genderArray[idx];
Object.assign(this.initArray[idx], { gender });
... or just bite the bullet and do assignment straight as we did in the good old syntax-sugar-free days:
this.initArray[idx].gender = this.genderArray[idx].gender;
In this particular case the last one looks like the best approach actually. Its disadvantage is having to repeat the property name, but that might also come handy if you want the props to be named differently in source and target arrays.
You're actually trying to "merge" objects in two arrays.
There are many ways to implement this, but I think the clearest one is with the Array.map and Object.assign functions:
const initArray = [
{ name: 'John', family: 'Doe'},
{ name: 'Joanna', family: 'Doe'}
];
const genderArray = [
{ gender: 'Male' },
{ gender: 'Female' }
];
const ans = initArray.map((person, index) => {
const genderedPerson = Object.assign({}, person, genderArray[index]);
return genderedPerson;
});
console.log(ans);
Array.map iterates an array and modifies its elements.
Object.assign extends an object, adding it additional properties from another object(s).
You can merge two arrays by reduce method using ... spread operator:
const result = initArray.reduce((a, c, i) => {
a.push({...c, ...genderArray[i]})
return a;
},[])
or one line version:
const result = initArray.reduce((a, c, i) => (a.push({...c, ...genderArray[i]}), a),[])
An example:
let initArray = [
{ name: 'John', family: 'Doe'},
{ name: 'Joanna', family: 'Doe'}
];
let genderArray = [
{ gender: 'Male' },
{ gender: 'Female' }
];
const result = initArray.reduce((a, c, i) => {
a.push({...c, ...genderArray[i]})
return a;
},[])
console.log(result)
The problem is that you're trying to use array.push() on an object, which doesn't work.
Instead, you could use a for loop to go through the array, and simply add a 'gender' category to that index by assigning a value to it, namely the gender at that index in the other array:
initArray = [
{ name: 'John', family: 'Doe'},
{ name: 'Joanna', family: 'Doe'}
];
genderArray = [
{ gender: 'Male' },
{ gender: 'Female' }
];
for (var i = 0; i < initArray.length; i++) {
initArray[i].gender = genderArray[i].gender;
}
console.log(initArray);
Alternatively, you could use array.forEach():
initArray = [
{ name: 'John', family: 'Doe'},
{ name: 'Joanna', family: 'Doe'}
];
genderArray = [
{ gender: 'Male' },
{ gender: 'Female' }
];
initArray.forEach(addGender);
function addGender(_, i) {
initArray[i].gender = genderArray[i].gender;
}
console.log(initArray);

Javascript: Finding largest value in object array and returning the key [duplicate]

This question already has answers here:
Get object from array by max property
(2 answers)
Closed 4 years ago.
I need to write a code that will look through the data array, and return the name of the oldest person.
console.log('The oldest person is ${oldestPerson}.')
let data = {
{
name: Henry,
age: 20,
job: 'store clerk'
},
{
name: Juliet,
age: 18,
job: 'student'
},
{
name: Luna,
age: 47,
job: 'CEO'
},
So far, I'm able to return separate arrays containing the names and ages, but I'm not sure how to find the oldest age and return name of the oldest person.
let names = data.map((person) => {
return person.name
});
let ages = data.map((person) => {
return Math.max(person.age)
});
Using reduce
let data = [{
name: ' Henry',
age: 20,
job: 'store clerk'
},
{
name: 'Juliet',
age: 18,
job: 'student'
},
{
name: 'Luna',
age: 47,
job: 'CEO'
}
];
var max = 0;
console.log(data.reduce((acc, a) => {
if (a.age > max) {
acc = a.name
max = a.age;
}
return acc;
}, ""))
To get the max age, you've got no choice but to loop over the entire array (and get the oldest one). So while you're looping over the array, grab the name too.
This is one approach of many:
let highestAge = 0;
const oldestName = data.reduce((prev,curr) => {
if(curr.age > highestAgo) {
highestAge = curr.age;
return curr.name;
}
return prev;
},'');

Get key/value from object, perform function and send back in

I am getting [{id:'test', time: '1223'}, {id: 'test2', time: '2323'}] from the backend, I need to retrieve the time values for all, perform a function and send back into the object as [{id:'test', time: 'new value for test'}, {id: 'test2', time: 'new value for test2'}]
So I decided I would try and extract the time values and push into array using for loop but my for loop is missing something
var objext = [{id:'test', time: 'blah'}, {id: 'test2', time: 'blah'}];
var dataArray = new Array;
for (var id in objext) {
dataArray.push(objext[id]);
};
It returns the same obviously as I don't know how to select the time key.
And then I'll run my function for each as such which is working well:
var time = [23,34,56];
var map = time.map(function(num) {
if (num === 23) {
return num +2;
} else {
return num;
};
});
And then the final part will be to put back and I'm thinking I could get an array of id and one of time and then slot back in together but the code I have so far can only map one array at a time:
var data = ["Drake","Ola","d"],
result = data.map(function (a) { return { id: a }; });
I have tried using an array as such:
var data = ["Drake","Ola","d"];
var hht = [21,34,56];
result = [data,hht].map(function (a,b) { return { id: a, time:b }; });
It did not map it properly.
Any ideas?
You can perform all of the tasks withing .map() callback
var objtext = [{id:'test', time: '1223'}, {id: 'test2', time: '2323'}];
objtext = objtext.map(function(obj) {
var time = obj.time;
// do stuff with `obj.time`
if (time == 1223) {
time += 2;
obj.time = time;
}
if (time == 2323) {
time -= 2;
obj.time = time;
}
return obj
});
To create an array containing only time properties
var times = objtext.map(function({time}) {return time});
You could use an array, iterate it and use from the other parts as well for a new object.
var data = ["Drake", "Ola", "d"],
hht = [21, 34, 56],
result = data.map(function (a, i) {
return { id: a, time: hht[i] };
});
console.log(result);
You can map the array of objects and return a different time with Object.assign:
var objs = [
{id: 'Drake', time: 'blah'},
{id: 'Ola', time: 'blah'},
{id: 'd', time: 'eh' }];
var hht = [21, 34, 56];
objs = objs.map( (obj, i) => Object.assign({}, obj, { time: hht[i] }) );
console.log(objs);
Try this using for loop.
var res = [{
id: 'test',
time: '1223'
}, {
id: 'test2',
time: '2323'
}, {
id: 'test3',
time: '2453'
}],
hht = [21, 34, 56];
for (var i = 0; i < res.length; i++) {
res[i]["time"] = +res[i]["time"] + hht[i];
//or use if statements
//if(+res[i]["time"] === 23)res[i]["time"] = +res[i]["time"] + hht[i];
//scenario will be
//res[0]["time"] = 1223 + 21
//res[1]["time"] = 2323 + 34
//res[2]["time"] = 2453 + 56
}
console.log(res);

Categories