I'm receiving data from the GMail API using $.ajax() with dataType: "xml", then throwing it into $.xml2json, which is a jQuery plugin. The problem is (as ironically demonstrated by the demo on the page) that when I put in something like this:
<animals>
<dog>
<name>Rufus</name>
<breed>labrador</breed>
</dog>
<dog>
<name>Marty</name>
<breed>whippet</breed>
</dog>
<cat name="Matilda"/>
</animals>
I get this:
{
animals: {
dog: [
{
name: 'Rufus',
breed: 'labrador'
}, {
name: 'Marty',
breed: 'whippet'
}
],
cat: {
name: 'Matilda'
}
}
}
Anyway, as you can see, in <animals> there are two <dog>s and only one <cat>; therefore, animals["cat"] is an object, while animals["dog"] is an array of objects. Likewise, when there is only one <entry> returned by GMail, feed["entry"] is an array of what is inside it, but when there is more than one tag, feed["entry"] is an array of entries, which in turn are arrays of what is inside them.
What I want to do is do different things, depending on whether I have 1 or more entries, using a conditional statement; to do that I need a boolean which tells me how many entries there are. How would I go about doing that? JQuery welcome.
EDIT: I guess another possible answer would be to edit the source of the plugin itself and make it offer some sort of way of knowing if it is a lone tag (an array-like object) or if there are lots of them (a "true" array).
I think the best thing you can do here is to simply concat the object/array to an empty array:
var obj = {a:"hello"};
[].concat(obj); // [{a:"hello"}]
var arr = [{a:"hello"}];
[].concat(arr); // identical results
So to fix your object:
for ( var prop in obj ) {
obj[prop] = [].concat(obj[prop])
}
This way you will always get an array and don't have to worry about which one you have.
var json = {
animals: {
dog: [
{
name: 'Rufus',
breed: 'labrador'
}, {
name: 'Marty',
breed: 'whippet'
}
],
cat: {
name: 'Matilda'
}
}
};
for ( var i in json ) {
if ( json.hasOwnProperty( i ) ) {
if ( json[i] instanceof Array ) {
// you have an array, do as you please
} else {
// you have an object, do as you please
}
}
}
You can use jQuery type() method to streamline your code and take advantage of fully cross browser compliant tests
API Refernce : http://api.jquery.com/jQuery.type/
$.each(data, function(i, item) {
switch($.type(item)) {
case 'string':
/* string code*/
break;
case 'array':
/* aray code*/
break;
/* ....etc*/
}
});
That is a bug in xml2json, and more generally a problem with converting XML to JSON--without a description document like XSLT or XSD, there's no way to tell the serializer that something is supposed to be a collection. You might want to look into something like this for turning XML into JSON in a more formal manner.
In the meantime, here's a way to force a variable to be an array if it isn't and leave it unchanged if it is:
x = [].concat(x);
Note: this was edited after the fact to use #Asad's superior method from his answer
This starts to fail with multidimensional arrays, as you've observed, so it sounds like you'd better start using XSLT to describe which elements are always collections.
And don't use instanceof
There is an edge case where instanceoffails. The instanceof operator used like x instanceof Array tests whether a particular constructor (in this case window.Array) is present in the prototype chain. But this fails when the variable came from another window, such as a popup window or an iframe that exists on the same domain and can pass objects directly to its parent.
Related
I have not been able to find a solution for this on StackoverlFlow and i have been working on this issue for some time now. Its a bit difficult so please let me know if i should provide more information.
My problem:
I have a JSON object of Lands, each land has an ID and a Blocks array associated with it and that blocks array has block with ID's too.
Preview:
var Lands = [{
'LandId':'123',
'something1':'qwerty',
'something2':'qwerty',
'Blocks': [
{
'id':'456',
'LandId':'123'
},
{
'BlockId':'789',
'LandId':'123'
}
]
},
...More Land Objects];
Note: The data is not setup the way i would have done it but this was done along time ago and i have to work with what i have for right now.
I am trying to write a lodash function that will take the blockId's that i have and match them and return the landId from the Blocks.
so the end result would be a list of LandId's that where returned from the Blocks.
I was using something like this and it was returning no results:
selectedLand = function(Lands, landIDs){
return _.filter(Lands, function(land){
return land === landIDs[index];
});
};
I know i am going about this the wrong way and would like to know the appropriate way to approach this and solve it. any help is much appreciated.
selectedLand = function(Lands, landIDs){
return _.filter(Lands, function(land){
return land === landIDs[index];
});
};
Note that index lacks any definition in this scope, so this function will essentially never return true, barring a lucky(?) accident with an externally defined index. And anytime you call _.filter(someArr,function(){return false;}) you'll get []. Undefined index aside, this does strict comparison of a land object against (maybe) a string in landIDs
I'm a bit unclear on the exact requirements of your selection, so you can tailor this filter to suit your needs. The function filters the lands array by checking if the .blocks array property has some values where the .landID property is included in the landsID array.
In closing, if you want to make the most out of lodash (or my favorite, ramda.js) I suggest you sit down and read the docs. Sounds deathly boring, but 75% of the battle with data transforms is knowing what's in your toolbox. Note how the English description of the process matches almost exactly with the example code (filter, some, includes).
var Lands = [{
'LandId': '123',
'something1': 'qwerty',
'something2': 'qwerty',
'Blocks': [{
'id': '456',
'LandId': '123'
}, {
'BlockId': '789',
'LandId': '123'
}]
}
];
// assuming lands is like the above example
// and landIDs is an array of strings
var selectLandsWithBlocks = function(lands, landIDs) {
return _.filter(lands, function(land) {
var blocks = land.Blocks;
var blockHasALandId = function(block) {
return _.includes(landIDs,block.LandId);
};
return _.some(blocks, blockHasALandId);
});
};
console.log(selectLandsWithBlocks(Lands,[]));
console.log(selectLandsWithBlocks(Lands,['mittens']));
console.log(selectLandsWithBlocks(Lands,['123']));
<script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/4.13.1/lodash.min.js"></script>
I'm trying to retrieve a nested object based on a nested nested id.
So my object is as follows
object = {
1: {
feature: {id:"1012"},
}
2: {
feature: {id:"3032"}
}
}
I have an id and I need to retrieve the corresponding object or to be more specific the object id. The object is a lot more complex but above shows the values that I need to retrieve.
I don't have much experience in JavaScript so am unsure how to achieve this. I've tried using Jquery's attribute selectors but have not been successful.
Any help would be appreciated.
Thanks.
if your "id" is mean like 1 or 2
do it like this:view it in JSFiddle
var obj = {
1: {
feature: {id:"1012"}
},
2: {
feature: {id:"3032"}
}
}
var getById = function(id){
return obj[id];
}
alert(getById(1).feature.id);
another way,if your id means like '1012','3032'
do it like this:view it in JSFiddle
my post about the Map in js
If I'm understanding the question correctly you are trying to use the id property of the object in each feature property to get the key (1, 2, etc) from object? So if you entered "1012" you would get back 1, if you entered "3032" you would get 2, etc?
If so this would do it:
var object = {
1: {
feature: {id:"1012"}
},
2: {
feature: {id:"3032"}
}
},
getIdByFeatureId = function (featureId) {
var id,
subObject;
// loop through each property of the object
for (id in object) {
// protect ourselves in case someone has tampered with Object.prototype
if (object.hasOwnProperty(id)) {
subObject = object[id];
if (subObject.feature.id === featureId) {
return id;
}
}
}
// none found? return null.
return null;
};
getIdByFeatureId("3032"); // returns 2
getIdByFeatureId("1012"); // returns 1
getIdByFeatureId("90210"); // returns null
You can play with the code in this fiddle.
Numbers stored as strings can be a pain, and often lead to confusion in how one need to call a function like this. One thing you might notice is I used the === strict equal operator. This only returns true if both values are exactly the same, including their type. It's good practice to use strict comparison operators unless you absolutely can't. It is also slightly faster since it doesn't have to coerce the values into a like type. But that means that you must pass a string into the function in order for it to match. You could use the non-strict equals == if you need it to be more flexible. If all of the feature ids are numeric (and none of them have leading zeros) and you have the ability to, I would change the feature ids to be actual numbers so you can keep it more intuitive by just passing in a number instead of a string representation of a number: getIdByFeatureId(3032); while keeping the strict comparison.
In my node REST application I have a function that queries a database for several records and returns an array of objects.
Since I want it to return a JSON object, I need a way to convert the array of objects to a single object with all the records inside.
Unfortunately I can't find an example on the internet about doing something like this.
Any help would be appreciated.
Why would you want to do that ? Its totally fine to JSON stringify an Array of items, you'll get a structure like
"[{},{},{},...]"
that is probably even an advantage, because you keep the order of items guaranteed.
See the object function of underscore.js.
Lets assume you have an array of objects with the form:
log {
name: "foo",
log: "bar"
}
Your could do:
var logs,//Array of logs
logObj = {}
for(i=0, i<logs.Length i++) {
logObj[logs[i].Name] = logs[i].log;
}
After the loop logObj should be:
logObj {
foo: bar,
nextName: cool comment,
etc.
}
I'm working on a Dashboard widget and I came across a code that looks like this:
var variableName = {
"SomeName":"someValue",
"someName": "another value",
};
That's pretty much a sum of what it looks like. My question is what is it, how does it work and what can I do with it? An example would be perfect. Thanks in advance!
That's an object literal. It's pretty much just like this:
var variableName = new Object();
variableName.SomeName = "someValue";
variableName.someName = "another value";
This is an example of an object literal.
It creates a normal object with two properties.
While it's called an object literal in JavaScript, it acts more like an enum in most languages.
var messageState = {
new: 0,
read: 1,
deleted: 2
};
With that in place, you have an easy to read way of determining message state:
var message = GetMessage();
if (message.state == messageState.deleted) {
alert('This message is deleted');
}
This is also an easy way to organize functional pieces within your JS file. If you want to only use one JS file for you entire site, which is highly recommended for all kinds of optimization, you can use this instead of writing several different functions:
var Message {
sendMessage: function(msg) {
// method to send msg
},
deleteMessage: function(msg) {
// method to delete msg
}
};
var Vote = {
votePostUp: function(post) {
// method to vote post up
},
votePostDown: function(post) {
// method to vote post down
}
};
And to call:
Message.sendMessage(theMessage);
Vote.votePostUp(myPost);
This is a JavaScript object created with object literal notation.
You can access its properties like this:
variableName["SomeName"];
or
variableName.SomeName;
You can also iterate over the properties of said object (in arbitrary order) with a for...in loop:
for(var prop in variableName) {
alert(prop + " = " + variableName[prop]);
}
For an excellent guide of working with JavaScript objects, check out this MDN article on working with objects.
What you are looking at is an associative array (a hash map or a dictionary in some languages). Basically it's an array that associates an object with another object, like a word is an associated with its definition in a real dictionary.
It's also JavaScript's main form of objects (they associate function names with function bodies).
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])
})