So I get a JSON request that sends an array of objects with two properties, I need to extract the unique values and their quantities.
This is the JSON that is being sent via Postman:
[
{"name": "First value", "amount": 2},
{"name": "Second value", "amount": 4},
{"name": "First value", "amount": 6}
]
I need to return a JSON response with the unique values and their totals added up:
The object should look like this:
{
"First value": 8,
"Second value": 4
}
You can use reduce()
const arr = [
{"name": "First value", "amount": 2},
{"name": "Second value", "amount": 4},
{"name": "First value", "amount": 6}
]
const res = arr.reduce((ac, {name, amount}) => {
ac[name] = ac[name] || 0;
ac[name] += amount;
return ac;
},{})
console.log(res)
Explanation:
First of all we are initializing ac to an empty object {}. See the line
arr.reduce((ac, {name, amount}) => {...}
^^^^^^^^^^^^^^
The highlighted part is called Object destructuring. It will get the property name and amount out the current object through which we are iterating and make it independent variables.
See the line
ac[name] = ac[name] || 0;
Now this line is checking if ac[name] doesn't exist on the ac object then it will be undefined so undefined || 0 will be evaluate to 0. If it will have a value the value of ac[name] will remain as previous.
See the third line:
ac[name] += amount;
This line will add the amount to already value of ac[name]
At last we return ac so that it will be become initial value of ac for next iteration.
You could also simply use Array.forEach as it is for the most part easier to understand and follow:
let arr = [{ "name": "First value", "amount": 2 }, { "name": "Second value", "amount": 4 }, { "name": "First value", "amount": 6 } ],
obj={}
arr.forEach(({name, amount}) => {
obj[name] = obj[name] || 0
obj[name] += amount
})
console.log(obj)
The main difference here is that our accumulator (the obj) is defined outside rather than "internally" as an argument to the Array.reduce function.
Related
I have list of objects in an array. How can i filter out if any one of key property of the object is null/empty. In given example the first record is perfect and the 2nd and 4th record have missing properties. So my ideal output should be 1st & 2nd.
[{
"ServiceArea": "NON-CIO",
"ManagingDirector": "qwe",
"Lead": "abc",
"Manager": "xyz"
"id":"1",
"Designation":"COO"
},
{
"ServiceArea": "NON-CIO",
"ManagingDirector": "dfg",
"Lead": "",
"Manager": "lkj"
"id":"2",
"Designation":"CTO"
},
{
"ServiceArea": "NON-CIO",
"ManagingDirector": "out",
"Lead": "poi",
"Manager": "",
"id":"43",
"Designation":"COO"
},
{
"ServiceArea": "4500-CIO",
"ManagingDirector": "yhh",
"Lead": "trr",
"Manager": "nbb"
"id":"403",
"Designation":"CTO"
}
]
Use Array.filter, then use Array.every to check whether every value is truthy.
const arr=[{ServiceArea:"NON-CIO",ManagingDirector:"qwe",Lead:"abc",Manager:"xyz",id:"1",Designation:"COO"},{ServiceArea:"NON-CIO",ManagingDirector:"dfg",Lead:"",Manager:"lkj",id:"2",Designation:"CTO"},{ServiceArea:"NON-CIO",ManagingDirector:"out",Lead:"poi",Manager:"",id:"43",Designation:"COO"},{ServiceArea:"4500-CIO",ManagingDirector:"yhh",Lead:"trr",Manager:"nbb",id:"403",Designation:"CTO"}];
const res = arr.filter(e => Object.values(e).every(k => !!k))
console.log(res)
If you want to filter by specific keys, you can store the keys in an array and use Array.every to check if all values are truthy.
const arr=[{ServiceArea:"NON-CIO",ManagingDirector:"qwe",Lead:"abc",Manager:"xyz",id:"1",Designation:"COO"},{ServiceArea:"NON-CIO",ManagingDirector:"dfg",Lead:"",Manager:"lkj",id:"2",Designation:"CTO"},{ServiceArea:"NON-CIO",ManagingDirector:"out",Lead:"poi",Manager:"",id:"43",Designation:"COO"},{ServiceArea:"4500-CIO",ManagingDirector:"yhh",Lead:"trr",Manager:"nbb",id:"403",Designation:"CTO"}];
const keys = ["Lead"]
const res = arr.filter(e => keys.every(k => !!e[k]))
console.log(res)
I have an object with keys as dates in the following format "yyyy-mm-dd" (ex: 2020-08-14)
the value of each key is an array of objects with two attributes, name and value.
I need to find a way to get the sum of the keyvalue grouped by name accros N days.
Here is an example to better understand, the original object have 4 days of data, with one day having an empty array:
{
"2020-10-15":[
{"name":"France","value":20},
{"name":"United States","value":20},
{"name":"Italy","value":5},
],
"2020-10-16":[
{"name":"Germany","value":10},
{"name":"United States","value":5},
{"name":"France","value":10}
],
"2020-10-17":[],
"2020-10-18":[
{"name":"UK","value":10},
{"name":"Italy","value":10},
]
}
For example if we wish to group this data by 4 days we will get the following:
[{"name": "France", "value": 30},
{"name": "United States", "value": 25},
{"name": "Italy", "value": 15},
{"name": "Germany", "value": 10},
{"name": "UK", "value": 10}]
This is the sum of all objects with same value for name across 4 days. I have absolutly no idea how to achieve this, I know can use map to iterate the object keys and do some processing through moment js but I don't know how to achieve this.
You can do the following using reduce, forEach and Object.keys method,
const value = {
"2020-10-15":[
{"name":"France","value":20},
{"name":"United States","value":20},
{"name":"Italy","value":5},
],
"2020-10-16":[
{"name":"Germany","value":10},
{"name":"United States","value":5},
{"name":"France","value":10}
],
"2020-10-17":[],
"2020-10-18":[
{"name":"UK","value":10},
{"name":"Italy","value":10},
]
}
let res = Object.keys(value).reduce((prev, curr) => {
value[curr].forEach(item => {
const idx = prev.findIndex(pItem => pItem.name === item.name);
if(idx > -1) {
const newObj = {...prev[idx], value: prev[idx].value + item.value};
prev[idx] = newObj;
return ;
}
prev.push(item);
})
return prev;
}, []);
console.log(res);
I am trying to figure out an easy way to convert an array of objects to an object
I have an array of objects that looks like this:
[
{
"id": "-LP9_kAbqnsQwXq0oGDT",
"value": Object {
"date": 1541482236000,
"title": "First",
},
},
.... more objects here
]
And id like to convert it to an object with the timestamps as the keys, and arrays of objects corresponding to that date. If that key already exists, then add the object to the corresponding array associated with that key
{
1541482236000:
[{
"id": "-LP9_kAbqnsQwXq0oGDT",
"value": Object {
"date": 1541482236000,
"title": "First",
},
},
{
"id": "-LP9_kAbqnsQwXqZZZZ",
"value": Object {
"date": 1541482236000,
"title": "Some other title",
},
},
.... more objects here
],
1541482236001:
[{
"id": "-LP9_kAbqnsQ1234",
"value": Object {
"date": 1541482236001,
"title": "Another title",
},
},
.... more objects here
]
}
I was able to achieve something similar using reduce. However it does not handle adding objects to the array when their key already exists.
calendarReminders = action.value.reduce((obj, reminder) => {
dateKey = moment(reminder.value.date).format('YYYY-MM-DD')
obj[dateKey] = [reminder]
return obj;
}, {});
How can I do this?
You just need to check whether the object is already a key and if not add it with the value of an array. Then you can just push() into it:
let arr = [{"id": "-LP9_kAbqnsQwXq0oGDT","value": {"date": 1541482236000,"title": "First",},},{"id": "SomID","value": {"date": 1541482236000,"title": "Some other title",},},{"id": "A different ID","value": {"date": 1541482236001,"title": "A third title",},}]
let calendarReminders = arr.reduce((obj, reminder) => {
(obj[reminder.value.date] || (obj[reminder.value.date] = [])).push(reminder)
return obj;
}, {});
console.log(calendarReminders)
If you want to set the keys to a different format with moment, you should be able to do that without changing the basic idea.
Please test the below code!
First you iterate through your array of data,
if your result object/dictionary already has the key then you just add the current item
otherwise you make the key and set the value
const data = [];
let result = {};
for (const item of data) {
const key = item.value.date;
if (result.hasOwnProperty(key)) {
const prevData = result[key];
result[key] = [...prevData, item];
} else {
result[key] = [item];
}
}
I am working with an API right now and I am using details[5].Value to target information in the following format:
details:
"value":[
{
"ID": "6",
"Name": "Links",
"Value": "URL"
},
{
"ID": "7",
"Name": "Other",
"Value": "URL"
}
etc
]
The problem is that the location inside of the JSON response is likely to change in the future, making my code obsolete and as the url has the potential to change as well, I cannot target that.
I want a way to target the value of url, mostly, because of this, by the value of the "Name" property. However, if I use something like
_.where(details, { Name: "Links" }).Value
It comes back as undefined. I am not sure if there would be another way to get to the information?
There are a couple points of confusion here.
_.where returns an array:
Looks through each value in the list, returning an array of all the values that contain all of the key-value pairs listed in properties.
so your _.where(details, obj).Value will (almost) always give you undefined because an array is unlikely to have a Value property. _.findWhere on the other hand does return a single value:
Looks through the list and returns the first value that matches all of the key-value pairs listed in properties.
Secondly, your details appears to look like:
let details = {
value: [
{ ID: '6', Name: 'Links', Value: 'URL' },
{ ID: '7', Name: 'Other', Value: 'URL' },
...
]
}
so you don't want to search details, you want to search details.value.
Putting them together:
_(details.value).findWhere({ Name: 'Links' }).Value
or
_.findWhere(details.value, { Name: 'Links' }).Value
You could use Array.prototype.find (or Array.prototype.filter if you're looking for all matches) and write your own callback but you already have Underscore available so why bother? Furthermore, Backbone collections have findWhere and where methods and there are advantages to matching Backbone's overall terminology.
Take a look at this mini function. Let me know if there is something wrong
Update
This is the ES5 Version
function f(key, value, array){
return array.value.filter(function(sub_array){
return sub_array[key] == value;
});
}
This is the ES6 Golfed Version
f=(k,v,a)=>a.value.filter(_=>_[k]==v)
//This is your JSON
var details = {
value: [
{
"ID": "6",
"Name": "Links",
"Value": "URL"
},
{
"ID": "7",
"Name": "Other",
"Value": "URL"
}
]
}
// Short code
f=(k,v,a)=>a.value.filter(_=>_[k]==v)
// f is the function name
// Recives k = array key, v = value, a = array
// filter by the given key and value
// return the result as an array
console.log(f('Name', 'Links', details))
An alternative is using the Javascript built-in function find to get a specific object within an array.
This alternative allows you to pass either an object or a string.
If the byThis parameter is an object, the whole set of key-values must match with the key-values of every object within the target array.
Otherwise, if byThis is a string every object will be treated as string to make the necessary comparison.
let details = { "value": [{ "ID": "6", "Name": "Links", "Value": "URL" }, { "ID": "7", "Name": "Other", "Value": "URL" }]};
let findBy = (array, byThis) => {
return array.find(o => {
if (typeof byThis === 'object') return Object.keys(byThis).every(k => o[k] === byThis[k]);
else if (typeof byThis === 'string') return o.toString() === byThis;
});
}
let found = findBy(details.value, {Name: "Links"});
console.log(found);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I am trying to get the same results as pythons json.dumps() with sort by keys enabled. This is preformed as a pre-request script for Postman to generate the request hash. The output needs to be sorted valid json which is used as input for hashing. I am new to javascript and see many old answers claiming that objects in javascript cannot be sorted. However there must be a solution to generate the hash given the criteria.
The object structure cannot be changed.
It only needs to support Chrome.
I can use libraries.
requestParams can contain nested objects and arrays which need to be sorted at any depth.
This is my current code. In the Chrome console the object preview for sortedResult is unsorted, however when I expand the object and sub-objects the Chrome console shows sortedResult as sorted, exactly the way it should be. This gives me the impression the sortObject is working. However requestOrdered returns the valid json object but it is not sorted. My initial thoughts are that maybe JSON.stringify() is unsorting it.
const requestRebuilt = {"username": user, "password": password, "sTime": time, "function": function,
"functionParams": requestParams, "salt": salt};
function sortObject(object){
var keys = _.keys(object);
var sortedKeys = _.sortBy(keys, function(key){
//console.log(key);
return key;
});
var sortedObj = {};
var sortedObjJson = "";
for(var index in keys){
var key = keys[index];
//console.log(key + ' ' + typeof object[key]);
if(typeof object[key] == 'object' && !(object[key] instanceof Array)){
sortedObj[key] = sortObject(object[key]);
} else if(object[key] instanceof Array) {
//sortedObj[key] = object[key].sort();
var arrayLength = object[key].length;
for (var i = 0; i < arrayLength; i++) {
sortedObj[key] = sortObject(object[key][i]);
//console.log(object[key][i]);
}
} else {
sortedObj[key] = object[key];
}
}
return sortedObj;
}
const sortedResult = sortObject(requestRebuilt);
console.log(sortedResult);
const requestOrdered = JSON.stringify(sortedResult);
console.log(requestOrdered);
var hash = CryptoJS.SHA256(requestOrdered).toString();
postman.setGlobalVariable("hash", hash);
Example input:
{
"username": "jdoe#mail.com",
"sTime": "2016-03-04T13:53:37Z",
"function": "begin",
"functionParams": {
"tip": "ABC123FFG",
"pad": 4 ,
"passenger": [{
"firstName": "John",
"phone": 1234567890,
"email": "jdoe#mail.com",
"dateOfBirth": "1915-10-02T00:00:00Z",
"bans": {
"weight": 9,
"count": 2
}
}
]},
"salt": "00d878f5e203",
"pep": "sdeODQ0T"
}
In python this is done by the following:
ordered = json.dumps(
{"username": user, "password": password, "time": time, "function": function, "functionParams": functionParams, "salt": salt}
sort_keys=True, separators=(',', ':'))
Result of ordered:
{"function":"begin","functionParams":{"passenger":[{"bans":{"count":2,"weight":9},"dateOfBirth":"1915-10-02T00:00:00Z","email":"jdoe#mail.com","firstName":"John","phone":1234567890}],"pad":4,"tip":"ABC123FFG"},"pep":"sdeODQ0T","salt":"00d878f5e203","sTime":"2016-03-04T13:53:37Z","username":"jdoe#mail.com"}
Pretty printed for easier reading but actual result should not have spaces or new lines:
{
"function": "begin",
"functionParams": {
"passenger": [
{
"bans": {
"count": 2,
"weight": 9
},
"dateOfBirth": "1915-10-02T00:00:00Z",
"email": "jdoe#mail.com",
"firstName": "John",
"phone": 1234567890
}
],
"pad": 4,
"tip": "ABC123FFG"
},
"pep": "sdeODQ0T",
"salt": "00d878f5e203",
"sTime": "2016-03-04T13:53:37Z",
"username": "jdoe#mail.com"
}
It's a common misconception that "object keys are not ordered" in javascript. MDN states that
Although ECMAScript makes iteration order of objects implementation-dependent, it may appear that all major browsers support an iteration order based on the earliest added property coming first (at least for properties not on the prototype).
and ES2015 makes this behaviour standard:
For each own property key P of O that is a String but is not an integer index, in property creation order...
That is, you can rely on the fact that object properties are always iterated in the insertion order (unless you're using delete, see here for details).
So, to sort keys in some object just create a new object and add keys to it in the sorted order:
function sortKeys(x) {
if (typeof x !== 'object' || !x)
return x;
if (Array.isArray(x))
return x.map(sortKeys);
return Object.keys(x).sort().reduce((o, k) => ({...o, [k]: sortKeys(x[k])}), {});
}
////
obj = {
"username": "jdoe#mail.com",
"sTime": "2016-03-04T13:53:37Z",
"function": "begin",
"functionParams": {
"tip": "ABC123FFG",
"pad": 4,
"passenger": [{
"firstName": "John",
"phone": 1234567890,
"email": "jdoe#mail.com",
"dateOfBirth": "1915-10-02T00:00:00Z",
"bans": {
"weight": 9,
"count": 2
}
}
]
},
"salt": "00d878f5e203",
"pep": "sdeODQ0T"
}
sorted = sortKeys(obj);
console.log(sorted);