Getting first JSON property - javascript

Is there a way to get the name of the first property of a JSON object?
I'd like to do something like this:
var firstProp = jsonObj[0];
edit: I'm getting a JSON object which hold categories of arrays with image
URLs.
like so:
{
"category1":["image/url/1.jpg","image/url/2.jpg"],
"category2":["image/url/3.jpg","image/url/4.jpg"]
}
I am then iterating through the object to insert the images, and all I really wanted was an elegant way to see which category was inserted first. At first I just did
for (var cat in images) {
if (i==0) firstCat = cat;
...
}
But that some how "felt" ugly... So it was basically just a question of elegance.

console.log(jsonObj[Object.keys(jsonObj)[0]]);

The order of the properties of an object are not guaranteed to be the same as the way you put them in. In practice, however, all major browsers do return them in order. So if you're okay with relying on this...
var firstProp;
for(var key in jsonObj) {
if(jsonObj.hasOwnProperty(key)) {
firstProp = jsonObj[key];
break;
}
}
Also note that there's a bug in Chrome regarding the ordering, in some edge cases it doesn't order it in the way they were provided. As far as it changing in the future, the chances are actually pretty small as I believe this is becoming part of the standard so if anything support for this will only become official.
All things considered, though, if you really, really, absolutely, positively, want to be sure it's going to be in the right order you need to use an array. Otherwise, the above is fine.
Related question: Elements order - for (… in …) loop in javascript

There isn't a "first" property. The properties of an object are unordered.
You can get whatever one the JS engine decides to provide first with a loop.
function maybe_first_in_object(ob) {
for (var props in ob) {
return prop;
}
}
… but if the order matters, use an array not an object.

Great question. I don't know of any way besides iterating in a for-in loop. You only need to iterate once though. For safety, ensure it's a known property [more info].
for (var propName in jsonObj) {
if (jsonObj.hasOwnProperty(propName)) {
return propName; // or do something with it and break
}
}
Edited to be extra clear that you're iterating over the property names, not their values.

When using jQuery, you can also use $.each to iterate over a JSON object. Looks cleaner IMO than using a for-loop
var items = {
'item1': 'content',
'item2': 'content',
'item3': 'content'
}
$.each(items, function() {
console.log($(this)[0])
})

Related

Most efficient way to filter through Javascript array of objects without standard keys

Let's say I have this (admittedly bad) structure:
var items = [
{SomeString: [value1,value2...]},
{AnotherString: [valueX,valueY...]},
{YetAnotherString: [valueA,valueB...]}
];
I want to obtain the array assigned to "AnotherString".
One way (via jQuery) I am using:
$.each(items,function(x,item){
$.each(item,function(key,values){
if(key=="AnotherString"){
//...do something
}
}
});
Obviously this is terribly inefficient, as it loops through needless items.
One way would be to refactor it in a "while(!found)" loop.
My question: what's the most efficient way to filter through such an array?
Edit: forgot to clarify that I do not know in advance the key strings. This is used in an external loop where the key strings I am looking for are passed down.
Sometimes, stripping away jQuery and using Vanilla JS is the route to the most efficient solution.
items.some(function(item) {
if( item.hasOwnProperty("AnotherString")) {
// do something with item["AnotherString"]
return true;
}
return false;
});
Documentation on the some function - Polyfill provided
If you know that this structure is bad, why do you stick to it? Just use a normal object:
var obj = {
SomeString: [value1,value2...],
AnotherString: [valueX,valueY...],
YetAnotherString: [valueA,valueB...]
};
If this is not possible for some reason, convert it dynamically:
obj = {};
items.forEach(function(item) {
var key = Object.keys(item)[0];
obj[key] = item[key];
});
This returns an object like the above. Once you have this, just do obj[someKey] when needed. No loops.
(This assumes, of course, that all keys are different).

Can I make a "Virtual Array" in JavaScript?

I'm calling a JavaScript function that wants an array of things to display. It displays a count, and displays the items one by one. Everything works when I pass it a normal JavaScript array.
But I have too many items to hold in memory at once. What I'd like to do, is pass it an object with the same interface as an array, and have my method(s) be called when the function tries to access the data. And in fact, if I pass the following:
var featureArray = {length: count, 0: func(0)};
then the count is displayed, and the first item is correctly displayed. But I don't want to assign all the entries, or I'll run out of memory. And the function currently crashes when the user tries to display the second item. I want to know when item 1 is accessed, and return func(1) for item 1, and func(2) for item 2, etc. (i.e., delaying the creation of the item until it is requested).
Is this possible in JavaScript?
If I understand correctly, this would help:
var object = {length: count, data: function (whatever) {
// create your item
}};
Then, instead of doing array[1], array[2], et cetera, you'd do object.data(1), object.data(2), and so on.
Since there seems to be a constraint that the data must be accessed using array indexing via normal array indexing arr[index] and that can't be changed, then the answer is that NO, you can't override array indexing in Javascript to change how it works and make some sort of virtual array that only fetches data upon demand. It was proposed for ECMAScript 4 and rejected as a feature.
See these two other posts for other discussion/confirmation:
How would you overload the [] operator in Javascript
In javascript, can I override the brackets to access characters in a string?
The usual way to solve this problem would be to switch to using a method such as .get(n) to request the data and then the implementor of .get() can virtualize however much they want.
P.S. Others indicate that you could use a Proxy object for this in Firefox (not supported in other browsers as far as I know), but I'm not personally familiar with Proxy objects as it's use seems rather limited to code that only targets Firefox right now.
Yes, generating items on the go is possible. You will want to have a look at Lazy.js, a library for producing lazily computed/loaded sequences.
However, you will need to change your function that accepts this sequence, it will need to be consumed differently than a plain array.
If you really need to fake an array interface, you'd use Proxies. Unfortunately, it is only a harmony draft and currently only supported in Firefox' Javascript 1.8.5.
Assuming that the array is only accessed in an iteration, i.e. starting with index 0, you might be able to do some crazy things with getters:
var featureArray = (function(func) {
var arr = {length: 0};
function makeGetter(i) {
arr.length = i+1;
Object.defineProperty(arr, i, {
get: function() {
var val = func(i);
Object.defineProperty(arr, i, {value:val});
makeGetter(i+1);
return val;
},
configurable: true,
enumerable: true
});
}
makeGetter(0);
return arr;
}(func));
However, I'd recommend to avoid that and rather switch the library that is expecting the array. This solution is very errorprone if anything else is done with the "array" but accessing its indices in order.
Thank you to everyone who has commented and answered my original question - it seems that this is not (currently) supported by JavaScript.
I was able to get around this limitation, and still do what I wanted. It uses an aspect of the program that I did not mention in my original question (I was trying to simplify the question), so it is understandable that other's couldn't recommend this. That is, it doesn't technically answer my original question, but I'm sharing it in case others find it useful.
It turns out that one member of the object in each array element is a callback function. That is (using the terminology from my original question), func(n) is returning an object, which contains a function in one member, which is called by the method being passed the data. Since this callback function knows the index it is associated with (at least, when being created by func(n)), it can add the next item in the array (or at least ensure that it is already there) when it is called. A more complicated solution might go a few ahead, and/or behind, and/or could cleanup items not near the current index to free memory. This all assumes that the items will be accessed consecutively (which is the case in my program).
E.g.,
1) Create a variable that will stay in scope (e.g., a global variable).
2) Call the function with an object like I gave as an example in my original question:
var featureArray = {length: count, 0: func(0)};
3) func() can be something like:
function func(r) {
return {
f : function() {featureArray[r + 1] = func(r + 1); DoOtherStuff(r); }
}
}
Assuming that f() is the member with the function that will be called by the external function.

How do I sort a JSON object by a nested value?

I have an ajax call that returns a JSON object that is pretty complex and I'm having a hard time sorting it.
My call:
$.post('/reports-ajax',arguments, function(data) {}
The response:
{
"10001":{
"unitname":"Fort Worth",
"discounts":{"12-02-2012":"34.810000","12-03-2012":"20.810000","12-04-2012":"27.040000"},
"gross":{"12-02-2012":"56.730000","12-03-2012":"19.350000","12-04-2012":"66.390000"},
"net":{"12-02-2012":"61.920000","12-03-2012":"98.540000","12-04-2012":"39.350000"},
"discounts_total":82.66,
"gross_total":82.47,
"net_total":99.81,
"number":10001
},
"10002":{
"unitname":"Dallast",
"discounts":{"12-02-2012":"12.600000","12-03-2012":"25.780000","12-04-2012":"47.780000","12-05-2012":"45.210000"},
"gross":{"12-02-2012":"29.370000","12-03-2012":"91.110000","12-04-2012":"60.890000","12-05-2012":"51.870000"},
"net":{"12-02-2012":"16.770000","12-03-2012":"65.330000","12-04-2012":"13.110000","12-05-2012":"06.660000"},
"discounts_total":131.37,
"gross_total":33.24,
"net_total":101.87,
"number":10002
},
"32402":{
"unitname":"Austin",
"discounts":{"12-05-2012":"52.890000","12-02-2012":"22.430000","12-03-2012":"58.420000","12-04-2012":"53.130000"},
"gross":{"12-05-2012":"25.020000","12-02-2012":"2836.010000","12-03-2012":"54.740000","12-04-2012":"45.330000"},
"net":{"12-04-2012":"92.200000","12-05-2012":"72.130000","12-02-2012":"13.580000","12-03-2012":"96.320000"},
"discounts_total":186.87,
"gross_total":161.1,
"net_total":174.23,
"number":32402
}
}
I go over the function with a standard each call and do some awesome stuff with highcharts but now I'm trying to sort the responses by the net_total call and I can't figure it out.
I tried .sort() and it errors out that its not a function. I've been reading for a while but guess I'm not finding the right results. This looked promising: Sorting an array of JavaScript objects but it failed with the .sort is not a function. It seems most .sort are on [] arrays not full objects..
Any help would be greatly appreciated.
Sorting objects doesn't make sense since object keys have no positional value. For example, this:
{ a:1, b:2 }
and this:
{ b:2, a:1 }
are exactly the same object. They're not just similar, they're the same.
Nothing in javascript per se gives object keys any positional value. Some people perhaps are mistaken in the belief that:
for (var key in obj) {
iterates through the object keys in a specific sequence. But this is wrong. You should always assume that the for .. in loop processes object keys in random order, always, all the time.
Obviously, if you're going to write a web browser, you're not going to implement a random number generator to parse a for .. in loop. Therefore most web browsers have an accidental stability to how the for .. in loop processes object keys.
Developers who learn javascript by playing around with the browser may figure out that their browser iterates through objects in alphabetical order for example, or the order the keys were added to the object. But this is totally accidental and cannot be relied upon. The browser vendor may change this behavior in the future without violating any backwards compatability (except with buggy scripts written by people who believe objects have a sort order). Not to mention that different browsers have different implementations of javascript and therefore not necessarily have the same internal key ordering of objects.
All the above is besides the point. "Key sort order" does not make any sense in javascript and any behavior observed is merely implementation detail. In short, javascript object does not have key order, just assume it's random.
Solution
Now, what you're really trying to do is not sort the object (you can't, it doesn't make sense). What you're really trying to do is process the object attributes in a specific order. The solution is to simply create an array (which has sorting order) of object keys and then process the object using that array:
// First create the array of keys/net_total so that we can sort it:
var sort_array = [];
for (var key in Response) {
sort_array.push({key:key,net_total:Response[key].net_total});
}
// Now sort it:
sort_array.sort(function(x,y){return x.net_total - y.net_total});
// Now process that object with it:
for (var i=0;i<sort_array.length;i++) {
var item = Response[sort_array[i].key];
// now do stuff with each item
}
What you have there isn't an array and has no order, so you'll have to transform it into an array so you can give it order.
Vaguely:
var array = [];
$.each(data, function(key, value) {
array.push(value);
});
array.sort(function(a, b) {
return a.net_total - b.net_total;
});
Live Example | Source
As GolezTroi points out in the comments, normally the above would lose the key that each entry is stored under in data and so you'd add it back in the first $.each loop above, but in this case the entries already have the key on them (as number), so there's no need.
Or you can replace the first $.each with $.map:
var array = $.map(data, function(entry) {
return entry;
});
// ...and then sort etc.
...whichever you prefer.

Javascript - get the value of a property within a specific JSON array element by its key

I have a JSON structure like this:
{
map: [
{"key1":"valueA1", "key2":"valueA2", "key3":"valueA3"},
{"key1":"valueB1", "key2":"valueB2", "key3":"valueB3"},
{"key1":"valueC1", "key2":"valueC2", "key3":"valueC3"},
.... etc
]
}
... which I load into my javascript app to become an object via JSON.parse().
I want to retrieve (say) the value of key3 from the element of the object array where key2='valueB2'.
I can do it by looping through, but wondered if there was a more elegant (e.g. single line and more efficient) way of doing this, without having to know the index number for the array element?
I've google loads of sites, to little avail. Or would I be better simplifying/removing the array in favour of a simple list of objects?
Thanks.
JSON will usually have double quotations " around all keys and values except for numbers.
However loop is the most efficient and best choice you have. There is new functional array iteration methods, but only available on new JS engines and browsers, they can be found here: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array
If you have the liberty of changing the JSON structure, you should implement it to be an Object instead of an array of objects where the key is the value of key2 and the value is the Object.
Example:
{
"valueB2": {
"key1": "valueB1",
"key2": "valueB2",
"key3": "valueB3"
}
}
The retrieval of the object would be O(1) and as simple as obj["valueB2"]
There isn't a more elegant way to do this. The only thing you know without looping are the indexes. That's not the identifier you want, so you'll have to inspect the content: loop:
function byKey(arr, key) {
for ( var i=0, L=arr.length; i<L; i++ ) {
if ( arr[i].key1 === key ) {
return arr[i];
}
}
}
Or something like that.
To round off: taking ideas from both answers, I adopted this simpler JSON structure:
[
{"key1":"valueA1", "key2":"valueA2", "key3":"valueA3"},
{"key1":"valueB1", "key2":"valueB2", "key3":"valueB3"},
{"key1":"valueC1", "key2":"valueC2", "key3":"valueC3"},
.... etc
]
with the suggested loop function to find the element for "key2:"valueB2" - the array is accessed by index for most of the time, and only occasionally by the loop-search, so that seemed the best balance. I also realised I could do away with the containing object "map" as it wasn't adding any utility.

What is the most efficient way to get the first item from an associative array in JavaScript?

I need to get just the first item (actually, just the first key) off a rather large associative array in JavaScript. Here's how I'm doing it currently (using jQuery):
getKey = function (data) {
var firstKey;
$.each(data, function (key, val) {
firstKey = key;
return false;
});
return firstKey;
};
Just guessing, but I'd say there's got to be a better (read: more efficient) way of doing this. Any suggestions?
UPDATE: Thanks for the insightful answers and comments! I had forgotten my JavaScript 101, wherein the spec says you're not guaranteed a particular order in an associative array. It's interesting, though, that most browsers do implement it that way. I'd prefer not to sort the array before getting that first key, but it may be unavoidable given my use case.
You can avoid having to create a function by referencing the first entry returned from Object.keys():
var firstKey = Object.keys(data)[0];
For the first entry from a sorted key list, simply add a call to the .sort() method:
var firstKey = Object.keys(data).sort()[0];
There isn't really a first or last element in associative arrays (i.e. objects). The only order you can hope to acquire is the order the elements were saved by the parser -- and no guarantees for consistency with that.
But, if you want the first to come up, the classic manner might actually be a bit easier:
function getKey(data) {
for (var prop in data)
return prop;
}
Want to avoid inheritance properties?
function getKey(data) {
for (var prop in data)
if (data.propertyIsEnumerable(prop))
return prop;
}
In addition to Jonathan's solution, we can also extend the default array functionality:
Array.prototype.getKey = function() {
for (var prop in this)
if (this.propertyIsEnumerable(prop))
return prop;
}

Categories

Resources