Comparing two arrays and fill in missing values - javascript

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)

Related

Filter nested JSON

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);

JavaScript: Compare dates in an array and sum the "price" for each month/year

I have a json file with multiple transactions with a date and a price attribute. Now I want to compare the dates and if they are in the same month and year I want to sum up the prices.
JSON:
transactions: [
{
date: "2017-11-17",
price: "28",
},
{
...
}
JavaScript:
request.onload = function() {
for(const transaction of request.response.transactions) {
let year = new Date(transaction.date).getFullYear();
let month = new Date(transaction.date).getMonth();
console.log(year + ' ' + month); // output: 2017-11 ...
}
};
I tried to loop over the json object but I struggle to find a solution to compare the dates.
Edit: Edited example with Object.assign instead of Object spread.
You'll need to use reduce to sum the prices. See comments for details.
const transactions = [{
date: "2017-11-17",
price: "28",
},
{
date: "2017-12-17",
price: "23",
},
{
date: "2017-11-17",
price: "12",
},
{
date: "2017-10-17",
price: "55",
},
{
date: "2017-11-17",
price: "09",
},
];
const sumTransactions = (transactions) => {
const summed = transactions.reduce((acc, current) => {
// Get the current date object
const date = new Date(current.date);
// Create your key/identifier
const key = `${date.getFullYear()}-${date.getMonth() + 1}`;
// Retreive the previous price from the accumulator
const previousPrice = acc[key]; // Might also return undefined
// Create your temp current price value, and be sure to deal with numbers.
let currentPrice = Number(current.price);
// If you had a previous value (and not undefined)
if (previousPrice) {
// Add it to our value
currentPrice += Number(previousPrice);
}
// Return the future accumulator value
return Object.assign(acc, {
[key]: currentPrice, // new values will overwrite same old values
})
}, {})
// Once we have all values, get the dates, and sort them (default: earlier first)
// Return an array of each value from the summed object to our sortedArray
const sortedArray = Object.keys(summed).sort().map((val) => {
return summed[val];
});
console.log("sortedArray", sortedArray);
};
sumTransactions(transactions);
I experimented a bit and came up with this solution:
var transactions = [
{
date: "2017-11-17",
price: "28",
},
{
date: "2017-12-17",
price: "22",
},
{
date: "2017-12-17",
price: "20",
}
]
var sumedUpDates = [];
var prices = [];
function isDateSumedUp(date) {
return sumedUpDates.indexOf(date.substring(0, 7)) !== -1;
}
function sumUpDate(date) {
var sum = 0;
transactions.forEach(t => {
if(t.date.substring(0, 7) === date.substring(0, 7)) {
sum += parseInt(t.price);
}
});
sumedUpDates.push(date.substring(0, 7));
prices.push(sum);
}
transactions.forEach(t => {
if(!isDateSumedUp(t.date)) {
sumUpDate(t.date);
}
});
var obj = {};
sumedUpDates.forEach((d, i) => obj[d] = prices[i]);
console.log(obj);
This solutions uses map to format your dates into year/month format for each object entry and then reduce to sum them by those separated dates.
const transactions = [
{date:"2017-11-17", price: "28",},
{date:"2017-12-17", price: "28",},
{date:"2017-11-17", price: "20",},
{date:"2017-12-17", price: "2",},
{date:"2017-11-17", price: "58",},
{date:"2017-11-17", price: "8",},
{date:"2017-10-17", price: "30",},
{date:"2018-11-17", price: "1",},
];
const mapper = single => {
let d = single.date.split('-');
let p = Number(single.price);
return { year: d[0], month: d[1], price: p };
}
const reducer = (group, current) => {
let i = group.findIndex(single => (single.year == current.year && single.month == current.month));
if (i == -1) {
return [ ...group, current ];
}
group[i].price += current.price;
return group;
};
const sumPrices = transactions.map(mapper).reduce(reducer, []);
console.log(sumPrices);
var array = [];
for (var i = 0; i < transactions.length; i++) {
var date = new Date(transactions[i].date);
var ym = date.getFullYear() + "-" + date.getMonth();
if (array[ym] == null) {
array[ym] = 0;
}
array[ym] += parseInt(transactions[i].price);
}
With this data
var transactions = [{
date: "2017-11-17",
price: "28",
},
{
date: "2017-12-17",
price: "5",
},
{
date: "2016-02-17",
price: "28",
},
{
date: "2015-11-17",
price: "25",
},
{
date: "2016-02-17",
price: "12",
},
{
date: "2017-11-17",
price: "50",
}
];
This will give you the sum of all of the year-months duplicates like this :
[
2017-10: 78,
2017-11: 5,
2016-1: 40,
2015-10: 25
]
Another solution is reduce:
var transactions = [
{date: "2017-11-17",price: "28"},
{date: "2017-12-17",price: "22"},
{date: "2017-12-17",price: "20"}
];
var result = transactions.reduce(function(acc, obj) {
var key = obj.date.substr(0,7);
acc[key] = (acc[key] || 0) + +obj.price;
return acc;
}, Object.create(null));
console.log(result);

Javascript how to display the amount of users based on which month they are created

I want to display the amount of users based on which month they are created. I got the following data as example:
[
{ name: 'user1', created: 'may' },
{ name: 'user2', created: 'may' },
{ name: 'user3', created: 'may' },
{ name: 'user4', created: 'may' },
{ name: 'user5', created: 'june' },
{ name: 'user6', created: 'june' },
{ name: 'user7', created: 'august' },
{ name: 'user8', created: 'august' },
{ name: 'user9', created: 'august' }
]
what I want to achieve is to display them like this:
may: 4
june: 2
august: 3
How can I do that?
You can use reduce() and return object as result.
var data = [{"name":"user1","created":"may"},{"name":"user2","created":"may"},{"name":"user3","created":"may"},{"name":"user4","created":"may"},{"name":"user5","created":"june"},{"name":"user6","created":"june"},{"name":"user7","created":"august"},{"name":"user8","created":"august"},{"name":"user9","created":"august"}]
var result = data.reduce(function(r, e) {
return r[e.created] = (r[e.created] || 0) + 1, r
}, {})
console.log(result)
I think this will give you the idea:
const usersCreatedInMay = users.filter(u => u.created == 'may')
usersCreatedInMay will return null or an array. Just look at the length of it to learn how many users created in may.
usersCreatedInMay.length
You can loop through all months and calculate how many users created in each month.
You could try something that goes in this direction, code may not work since it is not tested.
function extract(month, data) {
return let result = data.filter((res)=>{
return res.created === month;
})
This is a pure javascript code you can use:-
var monthWithUsercount = {};
for (var i = 0; i < a.length; i++) {
var num = a[i];
monthWithUsercount[num.created] = monthWithUsercount[num.created] ? monthWithUsercount[num.created]+1 : 1;
}
//Here "a" is the array consisting months and users.
console.log(monthWithUsercount);
Here is the working fiddle - https://jsfiddle.net/sajalsuraj/7nychnd2/
Cheers!!

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);

Manipulate a key value pair to a string in Javascript

I need some help in manipulating a value pair array to return a string in a specific layout
This is the string i am trying to achieve:
'&test=id:1111|na:shoes|ca:shoe&test=id:2222|na:top|ca:tops'
This is the array I am trying to manipulate into my string
var prodlist =
[
{
name: 'shoe',
sku: '1111',
category: 'shoes'
},
{
name: 'top',
sku: '2222',
category: 'tops'
}
]
Here is what I have tried.
I added the 'deleteme' into the array thinking i could to a substitute later down the script.
function(){
var prods = prodlist;
var array = [];
for (var i = 0; i < prods.length; i++) {
var sku = (prods[i]['sku']);
var name = (prods[i]['name']);
var cat = (prods[i]['category']);
array.push({
deleteme: &test=id,
id: sku,
na: name,
ca: cat,
});
}
var newarray = array.toString();
return newarray;
}
At the moment this function returns '[object Object],[object Object]'
any help would be much appreciated.
Thanks
Quick and easy
function prods() {
var prods = prodlist;
var array = [];
for (let product of prods)
array.push('test=id:' + product.sku+ '|na:' + product.name + '|ca:' + product.category);
return '&' + array.join('&');
}
how about something like this?
var prodlist =
[{name: 'shoe',
sku: '1111',
category: 'shoes'},
{name: 'top',
sku: '2222',
category: 'tops'}]
var strTemplate = "&test=id:%sku%|na:%name%|ca:%category%"
prodlist.map( function(obj){
return strTemplate.replace(/%(\w+)%/g, function($1, $2) {
return obj[$2]
})
}).join('')
//returns
// "&test=id:1111|na:shoe|ca:shoes&test=id:2222|na:top|ca:tops"
or ES6 version (edited it down further as suggested by nnnnnn )
prodlist.map( obj => strTemplate.replace(/%(\w+)%/g, ($1, $2) => obj[$2])).join('')

Categories