Finding the latest index of a particular key with inconsistent JSON? - javascript

Suppose I have some JSON like below:
[
{
"date": "2020-12-25",
"total_cases": 469482.0,
"new_cases": 2260.0,
"new_cases_smoothed": 2115.571,
"total_deaths": 9816.0,
"new_deaths": 63.0,
"new_deaths_smoothed": 80.857,
"total_cases_per_million": 2125.388,
"new_cases_per_million": 10.231,
"new_cases_smoothed_per_million": 9.577,
"total_deaths_per_million": 44.438,
"new_deaths_per_million": 0.285,
"new_deaths_smoothed_per_million": 0.366,
"new_tests": 54649.0,
"total_tests": 6482889.0,
"total_tests_per_thousand": 29.349,
"new_tests_per_thousand": 0.247,
"new_tests_smoothed": 38118.0,
"new_tests_smoothed_per_thousand": 0.173,
"positive_rate": 0.056,
"tests_per_case": 18.0,
"tests_units": "tests performed"
},
{
"date": "2020-12-26",
"total_cases": 471335.0,
"new_cases": 1853.0,
"new_cases_smoothed": 2006.714,
"total_deaths": 9874.0,
"new_deaths": 58.0,
"new_deaths_smoothed": 77.714,
"total_cases_per_million": 2133.777,
"new_cases_per_million": 8.389,
"new_cases_smoothed_per_million": 9.085,
"total_deaths_per_million": 44.701,
"new_deaths_per_million": 0.263,
"new_deaths_smoothed_per_million": 0.352,
"new_tests": 40953.0,
"total_tests": 6523842.0,
"total_tests_per_thousand": 29.534,
"new_tests_per_thousand": 0.185,
"new_tests_smoothed": 37101.0,
"new_tests_smoothed_per_thousand": 0.168,
"positive_rate": 0.054,
"tests_per_case": 18.5,
"tests_units": "tests performed"
},
{
"date": "2020-12-27",
"total_cases": 473309.0,
"new_cases": 1974.0,
"new_cases_smoothed": 2048.714,
"total_deaths": 9929.0,
"new_deaths": 55.0,
"new_deaths_smoothed": 76.714,
"total_cases_per_million": 2142.714,
"new_cases_per_million": 8.936,
"new_cases_smoothed_per_million": 9.275,
"total_deaths_per_million": 44.95,
"new_deaths_per_million": 0.249,
"new_deaths_smoothed_per_million": 0.347,
"new_tests": 33270.0,
"total_tests": 6557112.0,
"total_tests_per_thousand": 29.685,
"new_tests_per_thousand": 0.151,
"new_tests_smoothed": 36539.0,
"new_tests_smoothed_per_thousand": 0.165,
"positive_rate": 0.056,
"tests_per_case": 17.8,
"tests_units": "tests performed"
},
{
"date": "2020-12-28",
"new_tests": 32205.0,
"total_tests": 6589317.0,
"total_tests_per_thousand": 29.83,
"new_tests_per_thousand": 0.146,
"new_tests_smoothed": 36172.0,
"new_tests_smoothed_per_thousand": 0.164,
"tests_units": "tests performed"
}
]
Out of this array of records in the JSON, I'm looking to grab the most recent instance of "total_cases". Here is the JavaScript I wrote to satisfy this:
const total_cases = (data[data.length - 1].total_cases);
Obviously, this doesn't work, because the last record in the array doesn't contain an instance of the "total_cases" key. I'm fetching from the source using HTTP GET, and it is updated daily, so sometimes the latest record has the keys I'm looking for, but other times I get a TypeError.
My question is if there is any way to find the index of the latest record that has a particular key I'm looking for, when the JSON has some inconsistencies like this. The alternative is to say hell with it and look for another source. I'm considering that option, because this is too much of a hassle to work with, but I'm still curious as to what could be done. Hope that makes sense.

You could reverse the array and then use the Array.find method.
const total_cases = data
.slice() // Since reverse() changes the array in place, we need a copy of the original array
.reverse()
.find(record => record.total_cases !== undefined)
.total_cases;

A possible way to do that would be by reversing the array and then use Array.prototype.find() to search for the most recent record with total cases.
data.reverse().find(r => r.total_cases);

You can do something like this, that I find easy to understand for you and for the next dev reading your code
data
.sort((i1, i2) => i1.date > i2.date ? -1 : 1)
.filter(item => Boolean(item.total_cases))
[0].total_cases

Related

How to select json response item with spaces

I'm new to Javascript so this might be a naive question. I have following json response from the server. I want to select the values of Alpha, Bravo, Charlie and Delta.
data = {"Agent Contribution (Incl Rev)":{"Alpha":33245.0,"Bravo":26618.0,"Charlie":22376.0,"Delta":15831.0}}
I can select values in the console by doing data["Agent Contribution (Incl Rev)"]. However when I try this in JavaScript (code editor) by doing console.log(data["Agent Contribution (Incl Rev)"]), it returns null.
Try it this way:
data = {
"Agent Contribution (Incl Rev)": {
"Alpha": 33245.0,
"Bravo": 26618.0,
"Charlie": 22376.0,
"Delta": 15831.0
}
}
console.log(`
${data['Agent Contribution (Incl Rev)'].Alpha}
${data['Agent Contribution (Incl Rev)'].Bravo}
${data['Agent Contribution (Incl Rev)'].Charlie}
${data['Agent Contribution (Incl Rev)'].Delta}
`);
If I understood the question, you are looking for values of Alpha ,Bravo. if yes,Try this
var data = {"Agent Contribution (Incl Rev)":{"Alpha":33245.0,"Bravo":26618.0,"Charlie":22376.0,"Delta":15831.0}}
var selectedVal = Object.keys(data).map(function (prop) {
return Object.keys(data[prop]).map(function(val){
return data[prop][val]
})
})
console.log(selectedVal,"selectedVal")
There are many ways to access object properties, one of which is listed below:
First you extract keys using Object.keys which returns array of keys to you of a given object. You then iterate through each array item which is key of the given object and access using object property that key. Solutions like this would avoid all the potential typos in accessing key naming.
Its not a comprehensive solution to cover all edge cases but with keys having spaces, these is pretty good technique to cover all the bases. One thing to note here is key access is not in sorted order.
var data = {
"Agent Contribution (Incl Rev)": {
"Alpha": 33245.0,
"Bravo": 26618.0,
"Charlie": 22376.0,
"Delta": 15831.0
}
}
Object.keys(data).map(obj => {
console.log(data[obj]);
console.log(data[obj].Alpha);
console.log(data[obj].Bravo);
});

How to remove extra layer in JSON data from Firebase?

I'm trying to use Underscore.js to get some data from the JSON returned from Firebase but there are a lot of null values returned. The JSON being returned from Firebase currently looks like this:
{
"-JFnc68gIRFohkWKBP05":
{
"date":"2014-02-15T03:39:16.954Z",
"description":"Thanks so much!",
"name":"Rob",
"role":"Give"
},
"-JFncNSO4G_hNm0YySTA":
{
"date":"2014-02-15T03:40:27.858Z",
"description":"This is fun!",
"name":"Cobie",
"role":"Received"
},
"-JFrhlpgCWxJnDETM1gg":
{
"date":"2014-02-15T22:42:31.013Z",
"description":"Brought over some really delicious cookies",
"name":"John Smith",
"role":"Gave"
},
"-JFrjHlV-fsOVHyTXHZJ":
{
"date":"2014-02-15T22:49:08.448Z",
"description":"Charles was wonderfully patient.",
"name":"Charles Darwin",
"role":"Received"
},
"-JFsWZPbL6_j-9nQwP29":
{
"date":"2014-02-16T02:28:47.950Z",
"description":"On the Origin of Species... yaddi daddi da....",
"name":"Charles Darwin",
"role":"Gave"
},
"-JFsWdH61Y-I01-rqn_n":
{
"date":"2014-02-16T02:29:07.887Z",
"description":"Let me off to do my computer work without bugging me.",
"name":"Cobie",
"role":"Gave"
}
}
When I use Underscore.js to extract data, these are samples of how it comes out:
javascript:
$scope.allNames = _.pluck($scope.data, 'name');
output:
["","","","","","","","","","","","Rob","Cobie","John Smith","Charles Darwin","Charles Darwin","Cobie"]
javascript:
$scope.something = _.groupBy(userRef, 'userRef.name');
output:
{"undefined":[null,null,null,null,null,null,null,null,null,null,null,{"date":"2014-02-15T03:39:16.954Z","description":"Thanks so much!","name":"Rob","rating":5,"role":"Give"},{"date":"2014-02-15T03:40:27.858Z","description":"This is fun!","name":"Cobie","rating":4,"role":"Received"},{"date":"2014-02-15T22:42:31.013Z","description":"Brought over some really delicious cookies","name":"John Smith","rating":3,"role":"Gave"},{"date":"2014-02-15T22:49:08.448Z","description":"Charles was wonderfully patient.","name":"Charles Darwin","rating":"2","role":"Received"},{"date":"2014-02-16T02:28:47.950Z","description":"On the Origin of Species... yaddi daddi da....","name":"Charles Darwin","role":"Gave"},{"date":"2014-02-16T02:29:07.887Z","description":"Let me off to do my computer work without bugging me.","name":"Cobie","role":"Gave"}]}
How do I get deep enough into the JSON to not return the nulls? Or, If there is a better way to remove the headers from the JSON entirely?
Resolved the issue with first removing the empty strings using underscore's _.without function.
$scope.allcontacts = _.without(messy_contacts, "")
You can use underscore compact for this.
$scope.allcontacts = _.compact(messy_contacts);

Searching json array for a specific attribute

Actually I want to search an attribute's value in an json array for one of its child. Now one condition is that the attribute will not be there in all the child's of the array. This is my json array.
[{
"heading1":"heading1",
"heading2":"heading2",
"heading3":"heading3",
"heading4":"heading4",
"heading5":"heading5",
"heading6":"heading6"
},
{
"column1":65536,
"column2":"school",
"column3":"testing purpose",
"column4":"DESKTOP",
"column5":"ACTIVE",
"column6":true,
"column7":"a6cc82e0-a8d8-49b8-af62-cf8ca042c8bb"
},
{
"column1":98305,
"column2":"Nikhil",
"column3":"Test",
"column4":"LAPTOP",
"column5":"ACTIVE",
"column6":true,
"column7":"a6cc82e0-a8d8-49b8-af62-cf8ca042c8bb"
}]
So presently I am working with the each loop but like this
var obj = $.parseJSON(JSON.stringify(response));
$.each(obj, function () {
console.log("heading1", this['heading1']);
});
Here response comes from mserver and it is the json array
Now I want to know can I search for this attribute in the json array without using a loop in jQuery.
Based on your sample code what I understand you have is an array of objects and you want to find objects with one specific property and or value:
This will return true if the object has the property
var results= arr.filter(function(item){ return item.hasOwnProperty("column5"); });
Or you can perform additional action when you find the property:
arr.filter(function(item){
if (item.hasOwnProperty("column5")) {
return item["column5"] === 'demo 01'; //or item.column5 === 'demo 01'
}
return false;
});
This only works on IE9+ if you need this to run in older versions of IE, please follow the instructions under polyfill:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
The you can check like
var obj = $.parseJSON(response);
$.each(obj, function (index,value) {
if(typeof obj[index].heading2 !== "undefined")
{
alert(obj[index].heading2);
}
when in other object of array element not find then it returns undefined. and you can check like that.
you can check in this http://jsfiddle.net/gKRCH/
It's best to use a loop. But if the format of the JSON is regular, you could regex for the value in the response string.
I'm not recommending this method, just pointing out that it exists.
var value = "heading1";
if( (new RegExp('"' + value + '"')).test(response) ){
// Found value
};
Here, we take the required value, wrap it in quotation marks and search for it in the response.
This has several issues, such as:
It might find the pattern in a property name
If the value could contain regex special characters, they'll need escaping.
If your JSON contains values with escaped quotation marks, you could get a false positive from partial matches.
That's why it depends on you knowing the format of the data.
EDIT:
You can solve issue 2 by using this condition instead of regex. But it gives you less flexibility.
response.indexOf('"' + value + '"') !== -1
Try this,
$.each(object,function(key, value){
console.log(key);
console.log(value);
});
You can use this JS lib; DefiantJS (http://defiantjs.com). This lib extends the global object JSON with the method "search" - with which, you can perform XPath queries on JSON structures. Like the one you have exemplified.
With XPath expressions (which is standardised query language), you can find whatever you're looking for and DefiantJS will do the heavy-lifting for you - allowing your code to be neat and clean.
Here is the fiddle of this code:
http://jsfiddle.net/hbi99/q8xst/
Here is the code:
var data = [
{
"heading1": "heading1",
"heading2": "heading2",
"heading3": "heading3",
"heading4": "heading4",
"heading5": "heading5",
"heading6": "heading6"
},
{
"column1": 65536,
"column2": "school",
"column3": "testing purpose",
"column4": "DESKTOP",
"column5": "ACTIVE",
"column6": true,
"column7": "a6cc82e0-a8d8-49b8-af62-cf8ca042c8bb"
},
{
"column1": 98305,
"column2": "Nikhil",
"column3": "Test",
"column4": "LAPTOP",
"column5": "ACTIVE",
"column6": true,
"column7": "a6cc82e0-a8d8-49b8-af62-cf8ca042c8bb"
}
],
res = JSON.search( data, '//*[column4="DESKTOP"]' );
console.log( res[0].column2 );
// school

Change the name of keys while creating an array

I have the response in the following format after doing groupby in solr query. I am using solr version 3.5
"grouped":{
"channel_id":{
"matches":48,
"ngroups":26,
"groups":[{
"groupValue":"204",
"doclist":{"numFound":1,"start":0,"docs":[
{
"channel_name":"ZeeTv",
"channel_num":4,
"title":"The Name",
"channel_id":"204"
}},
{
"groupValue":"166",
"doclist":{"numFound":2,"start":0,"docs":[
{
"channel_name":"Sony",
"channel_num":2,
"title":"The Name",
"channel_id":"166",
{
"channel_name":"Sony",
"channel_num":2,
"title":"The Puzzle",
"channel_id":"166"
}}]}}
I am taking the response in an array in the following way :
for(var chl in data.grouped.channel_id.groups) {
config['playlist'].push(data.grouped.channel_id.groups[chl]['doclist']['docs']);
}
Thus an individual array of each groupValue is formed. The struture of the array is:
"0"=>{"0"=>"value"},"1"=>{"0"=>"result1","1"=>"result2"}
But i want to change the key name i.e. "0","1" to the groupValue from the response while creating an array so that i can do config['playlist']['166'] to check all the shows for this channel_id from the array. Can this be done and if so how. I am expecting the following :
"204"=>{"0"=>"value"},"166"=>{"0"=>"result1","1"=>"result2"}
Also if possible can the solr query be made such that the channel_num in the response comes in ascending order i.e. first result for channel_num 2 and then 4. I have done groupby:channel_id
What about that?
for(var chl in data.grouped.channel_id.groups) {
config['playlist'][data.grouped.channel_id.groups[chl].groupValue] = data.grouped.channel_id.groups[chl]['doclist']['docs'];
}
Push is there to add an element at the end of an array. But any Javascript object is just a hash table, so you can use it that way.
By the way, you can make the code simpler with a for each :
for each(var chl in data.grouped.channel_id.groups) {
config['playlist'][ch1.groupValue] = ch1['doclist']['docs'];
}
Pikrass has answered correctly... as far as ordering with channel_num is concerned try adding the following in your query:
&sort=channel_num asc

localstorage: Get specific localstorage value of which contains many items

In localstorage I have key 'results' with this values:
[{"id":"item-1","href":"google.com","icon":"google.com"},
{"id":"item-2","href":"youtube.com","icon":"youtube.com"},
{"id":"item-3","href":"google.com","icon":"google.com"},
{"id":"item-4","href":"google.com","icon":"google.com"},
{"id":"item-5","href":"youtube.com","icon":"youtube.com"},
{"id":"item-6","href":"asos.com","icon":"asos.com"},
{"id":"item-7","href":"google.com","icon":"google.com"},
{"id":"item-8","href":"mcdonalds.com","icon":"mcdonalds.com"}]
To get the last item I use this:
// this is how I parse the arrays
var result = JSON.parse(localStorage.getItem("result"));
for(var i=0;i<result.length;i++) {
var item = result[i];
$('element').val(item.href);
}
How can I get the href for item-3 or for a specific ID?
Using native Array.filter
If you are targeting only modern browsers (IE9+ or a recent version of any other major browser) you can use the JavaScript 1.6 array method filter.
var testItem,
data = [{"id":"item-1","href":"google.com","icon":"google.com"},
{"id":"item-2","href":"youtube.com","icon":"youtube.com"},
{"id":"item-3","href":"google.com","icon":"google.com"},
{"id":"item-4","href":"google.com","icon":"google.com"},
{"id":"item-5","href":"youtube.com","icon":"youtube.com"},
{"id":"item-6","href":"asos.com","icon":"asos.com"},
{"id":"item-7","href":"google.com","icon":"google.com"},
{"id":"item-8","href":"mcdonalds.com","icon":"mcdonalds.com"}];
function getItemById(data, id) {
// filter array down to only the item that has the id
// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/filter
var ret = data.filter(function (item) {
return item.id === id;
});
// Return the first item from the filtered array
// returns undefined if item was not found
return ret[0];
}
testItem = getItemById(data, 'item-3');
Working example
Manually looping over the data
If you can't use filter you are probably stuck with just using a loop:
var testItem,
data = [{"id":"item-1","href":"google.com","icon":"google.com"},
{"id":"item-2","href":"youtube.com","icon":"youtube.com"},
{"id":"item-3","href":"google.com","icon":"google.com"},
{"id":"item-4","href":"google.com","icon":"google.com"},
{"id":"item-5","href":"youtube.com","icon":"youtube.com"},
{"id":"item-6","href":"asos.com","icon":"asos.com"},
{"id":"item-7","href":"google.com","icon":"google.com"},
{"id":"item-8","href":"mcdonalds.com","icon":"mcdonalds.com"}];
function getItemById(data, id) {
var i, len;
for (i = 0, len = data.length; i < len; i += 1) {
if(id === data[i].id) {
return data[i];
}
}
return undefined;
}
testItem = getItemById(data, 'item-3');
Working example
Even though brute-forcing it with a loop might seem less elegant than using Array.filter, it turns out that in most cases the loop is faster than Array.filter.
Refactoring to an object instead of an array
The best solution, assuming that the id of each of your items is unique, would be refactoring the way you are storing the data. Instead of an array of objects, use an object that uses the id as a key to store an object containing the href and icon key/property values.
var data = {
"item-1": {"href": "google.com", "icon": "google.com"},
"item-2": {"href": "youtube.com", "icon": "youtube.com"},
"item-3": {"href": "google.com", "icon": "google.com"},
"item-4": {"href": "google.com", "icon": "google.com"},
"item-5": {"href": "youtube.com", "icon": "youtube.com"},
"item-6": {"href": "asos.com", "icon": "asos.com"},
"item-7": {"href": "google.com", "icon": "google.com"},
"item-8": {"href": "mcdonalds.com", "icon": "mcdonalds.com"}
};
This would make accessing items even easier and faster:
var data = JSON.parse(localStorage.getItem("result"));
data["item-3"].href;
jQuery has filter helper for that:
$(result).filter(function(){return this.id == "item-3";})[0]
Function for href of item with specific id would be:
function getItemHrefById(json, itemId){
return json.filter(function(testItem){return testItem.id == itemId;})[0].href;
}
And sample usage is:
var href = getItemHrefById(result, "item-3");
You can see working example on http://jsfiddle.net/LXvLB/
UPDATE
If you cannot read item from local storage, maybe you forgot to call JSON.stringify when setting value:
localStorage["results"] = JSON.stringify([{"id":"item-1","href":"google.com","icon":"google.com"},
{"id":"item-2","href":"youtube.com","icon":"youtube.com"},
{"id":"item-3","href":"google.com","icon":"google.com"},
{"id":"item-4","href":"google.com","icon":"google.com"},
{"id":"item-5","href":"youtube.com","icon":"youtube.com"},
{"id":"item-6","href":"asos.com","icon":"asos.com"},
{"id":"item-7","href":"google.com","icon":"google.com"},
{"id":"item-8","href":"mcdonalds.com","icon":"mcdonalds.com"}])
You need to convert json to string to be properly serialized (and to use JSON.parse to get JSON back)
This is final example.
EDIT
As Useless Code pointed out, this metod is substantially slower than native filter function (and custom loop but I think that introducing few new lines of code to save 20-30ms is overkill unless performance is a issue), so I'm updating my example to not use jquery filter. +1 please for his answer for that.
Also, what is important to point out here, if this array would have hundreds instead of 8 bookmarks, for loop would probably be statistically about twice faster (as it does not have to iterate through rest of the array). But, in that case it would probably be a good idea to put for loop into function which returns first found item which satisfies condition, and with prototypejs it probably can even be hooked up to array.
for the jquery filter method, I think using a callback function, and bind the search parameter is more elegant and readable:
function filterById(id, i, obj) {
return obj.id === id;
}
function getItemHrefById(json, itemId) {
return $(json).filter(filterById.bind(null, itemId))[0].href;
}
da usual fiddle
(however, i prefer the "for loop" approach" for this!!)

Categories