How to access fields in JSON object by index - javascript

I know this isn't the best way to do it, but I have no other choice :(
I have to access the items in JSONObject by their index. The standard way to access objects is to just wirte this[objectName] or this.objectName. I also found a method to get all the fields inside a json object:
(for (var key in p) {
if (p.hasOwnProperty(key)) {
alert(key + " -> " + p[key]);
}
}
(Soruce : Loop through Json object).
However there is no way of accessing the JSONfields directly by a index. The only way I see right now, is to create an array, with the function above, get the fieldname by index and then get the value by fieldname.
As far as I see it, the p (in our case the JSON file must be an iteratable array to, or else the foreach loop wouldn't work. How can I access this array directly? Or is it some kind of unsorted list?

A JSON Object is more like a key-value-map; so, yes, it is unsorted. The only way to get around is the index->property name map you've already mentioned:
var keysbyindex = Object.keys(object);
for (var i=0; i<keysbyindex.length; i++)
alert(object[keysbyindex[i]]);
But why would you need these indexes? A unsorted map also has no length property, as an Array had. Why don't you use the for-in-loop
var counter = 0; // if you need it
for (var key in object) {
alert(object[key])
counter++;
}
? If you have a parsed JSON object, i.e. a plain JS Object, you won't have to worry about enumerable prototype properties.

Based on Bergis anserwer this is my solution:
var keysbyindex = Object.keys(this);
alert(this[keysbyindex[index]]);
return this[keysbyindex[index] || ""];
However, I think (not tested) it's extremly bad regaring performace and shouldn't be used! But desperate times require desperate measures.....

I don't think you can actually achieve this without creating your own parsing of JSON. You're writing that you want to go trough a JSON-object, but what you're actually trying to do is go trough a plain old Javascript object. Json is simply a string-representation used to transfer/store said object, and in here lies the main problem: the parser that transforms the string into an actual object (ie. the browser in most cases) can chose to ignore the order it finds the properties if it want to. Also, different browsers might have different approaches to parsing JSON for all you know. If they simply use a hash-map for the object that it's simple to loop through it, but the order won't be dependent on the order of the keys in the file, but rather the keys themselves.
For example, if you have the json {"b":"b","a":"a"} and do the for in loop, under some implementations you might end up with a comming first, and in others you might end up with b.

var jsn = {keyName: 'key value result come here...'};
var arr = jsn ? $.map(jsn, function (el) { return el }) : [0];
console.log(arr[0])
$('.result').text(arr[0]);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span class="result"></span>

Related

Is it safe to rename the keys of an object while iterating them?

I'm renaming the keys of an object while iterating them:
Object.keys(object).forEach(function(oldKey) {
var newKey = someFunc(oldKey);
if (newKey !== oldKey) {
object[newKey] = object[oldKey];
delete object[oldKey];
}
}
And I would like to know if this method is safe.
In other words, can I be sure that my code will never iterate a key which has been renamed in a previous iteration?
No, you aren't safe. You're mutating the object live, based on an array that is not live. If you happen to cross a new name with an old (rename a to b, but b already exists and haven't been reached yet) you're going to have a bad time.
You will not come across keys you've already seen, but you have no way to know whether the newKey is not already found in the object.
There are workarounds, the situation is similar to .splice()ing an array (removing elements) while you iterate it, and the simple workaround is to iterate backwards, so that you always already pass the altered keys. (Or in your case, checking with the in operator)
You're much better, however, creating and returning a new object:
const newObj = Object.keys(object).reduce(function(result, oldKey) {
var newKey = someFunc(oldKey);
return { ...result, [newKey]: object[oldKey] };
}, {});
You get a lot of things for free when you treat all of your data structures as immutables (and more specifically, when the keys never change)
Object.keys, like many other methods, returns an Array that you can iterate over. This Array is not "live" but a snapshot from the time of taking it (e.g. executing Object.keys). So yes, you're save to use it as intended.
There are very little examples of methods that return "live lists" instead of an Array; I guess you're having NodeLists in mind, that you'll get when using document.querySelectorAll. This however not an Array but a NodeList.
However, there may be one pitfall I can see is: When a generated newKey already exists in the list of oldKeys (not the current one!). So you may or may not (depending on the position in the array) iterate over the already overwritten new key.
Here is a solution to change the key without creating a new Object.
for(key in obj){
Object.defineProperty(obj, `myNewName`, Object.getOwnPropertyDescriptor(obj, key));
delete obj[key];
}

Adding fields to JS array

I've added a field "max" to the JS array prototype. I'm having trouble seeing this addition when I stringify the array. I've tried to overwrite the toJSON function but to no avail.
Array.prototype.max = 0;
Array.prototype.toJSON = function(){
var o = this;
o.max = this.max;
return o;
};
var a = [1,2,3];
a.max = 10;
console.log(JSON.stringify(a));
//'[1,2,3]'
I'd like to avoid doing something like {array_field:a,max_field:max} and just use the array object - is this possible? What am I doing wrong?
You have a few options here, none of them will do exactly what you want.
Since JSON arrays are strictly limited to integer keys[1], you can't send down the value of max directly.
Your choices include:
Using an object instead of an array. You loose all of the array magic, but it might work better for you.
Send down meta-data outside your array, such as a configuration object that includes both max and your array values
Force the array to be max long before sending it down. JSON will encode unused array elements with null.
Use a custom serialzer/deserializer. You could even code things in JSONS + the extras. In reality, this pretty much turns out the same as the second option.
[1] Unlike JavaScript Objects

Array type being picked up as array value

I have some simple Javascript looping through an array of items (Tridion User Groups) to check if the user is a member of a specific group.
I can easily code around the issue shown below ( see && extensionGroup !== 'true') but I want to understand why the isArray = true is counted as a value in the array - any ideas?
The screenshot below demonstrates that the value extensionGroups has been set thus
var extensionGroups = ["NotEvenARealGroup", "Author", "ExampleGroupAfterOneUserIsActuallyIn"];
but returns the isArray value as a 4th value?
updated to show images a little clearer
You're using for in to iterate an array; don't do that. Use for (or forEach):
for(var i = 0; i < extensionGroups.length; i++) {
var extensionGroup = extensionGroups[i];
// ...
}
The reason this fails is because for in is used to iterate over an object's properties in JavaScript. Iterating over an array in this way means you get anything else assigned to it, such as this property or length.
And if you're able to use Array#forEach, it's probably most appropriate here:
extensionGroups.forEach(function(extensionGroup) {
// ...
});
For..in, technically speaking, doesn't iterate through values. It iterates through property names. In an array, the values ARE properties, under the hood. So when you iterate over them with for..in you get funky stuff like that happening.
Which highlights my next point: don't use for..in. Don't use it for arrays -- don't use it for anything, really. Ok -- maybe that's going a bit too far. How about this: if you feel the need to use for..in, think hard to see if it's justifiable before you do it.

Creating multi-dimensional arrays in javascript, error in custom function

I was trying to define an array (including other arrays as values) in a single javascript statement, that I can loop through to validate a form on submission.
The function I wrote to (try to) create inline arrays follows:
function arr(){
var inc;
var tempa = new Array(Math.round(arguments.length/2));
for(inc=0; inc<arguments.length; inc=inc+2) {
tempa[arguments[inc]]=arguments[inc+1];
}
return tempa;
}
This is called three times here to assign an array:
window.validArr = arr(
'f-county',arr('maxlen',10, 'minlen',1),
'f-postcode',arr('maxlen',8, 'minlen',6)
);
However in the javascript debugger the variable is empty, and the arr() function is not returning anything. Does anyone know why my expectations on what this code should do are incorrect?
(I have worked out how to create the array without this function, but I'm curious why this code doesn't work (I thought I understood javascript better than this).)
Well from what your code does, you're not really making arrays. In JavaScript, the thing that makes arrays special is the management of the numerically indexed properties. Otherwise they're just objects, so they can have other properties too, but if you're not using arrays as arrays you might as well just use objects:
function arr(){
var inc;
var tempa = {};
for(inc=0; inc<arguments.length; inc=inc+2) {
tempa[arguments[inc]]=arguments[inc+1];
}
return tempa;
}
What you're seeing from the debugger is the result of it attempting to show you your array as a real array should be shown: that is, its numerically indexed properties. If you call your "arr()" function as is and then look at (from your example) the "f-county" property of the result, you'll see something there.
Also, if you do find yourself wanting a real array, there's absolutely no point in initializing them to a particular size. Just create a new array with []:
var tempa = [];
Your code works. Just inspect your variable, and you will see that the array has the custom keys on it. If not expanded, your debugger shows you just the (numerical) indixed values in short syntax - none for you.
But, you may need to understand the difference between Arrays and Objects. An Object is just key-value-pairs (you could call it a "map"), and its prototype. An Array is a special type of object. It has special prototype methods, a length functionality and a different approach: to store index-value-pairs (even though indexes are still keys). So, you shouldn't use an Array as an associative array.
Therefore, their literal syntax differs:
var array = ["indexed with key 0", "indexed with key 1", ...];
var object = {"custom":"keyed as 'custom'", "another":"string", ...};
// but you still can add keys to array objects:
array.custom = "keyed as 'custom'";

Call Json by index?

I want to return some errors to my jquery method. What is happening is I am doing a post(with a type of "json") and if they get errors I want to display the error back to them. I am doing client side validation but some of these are errors that are like server related(ie the database died or something and that update could not happen).
Anyways there could be a few errors and I want to return them all at one go.
So the only way I know really how is to use Json but I now I get the json object I want to get all the fields out of it. I don't want to call it by their name though since I want to use the same methods for all my methods and each one has different names.
So if I could call it by index there would be alot less typing.
Can I do this?
Since you are using jQuery, you could use $.each to iterate over object properties, for example:
var obj = { one:1, two:2, three:3, four:4, five:5 };
jQuery.each(obj, function(key, val) {
console.log(key,val);
});
For objects jQuery internally executes a for...in statement, which does not iterate over built-in properties, however you can have problems if the Object.prototype is extended since that extended members will be iterated also.
Is not a common practice to extend the Object.prototype, but to avoid problems you can use the hasOwnProperty function to ensure that the property exist directly on the object being iterated:
for ( var key in obj) {
if (obj.hasOwnProperty(key)) {
console.log(key,obj[key]);
}
}
JSON is nothing more than yet another markup-language to describe complex datastructures. JSON gets parsed into javascript data-structures and can represent objects, arrays or just a string in theoretically unlimited depth.
Without knowing if your JSON structure consists of arrays, objects, or {} constructs it's hard to tell if you can.
However, you could have a look at:
var dataObject = {
key1: "error1",
key2: "error2"
};
for (var key in dataObject) {
alert(key + " : " + dataObject[key]);
}

Categories