What I essentially need to do is, given a json like
{
"1" : "a",
"7" : "something"
"3" : {
"1" : "blah"
}
}
Convert these to an array (say x).
x[1] = "a"
x[7] = "something"
x[3] = y (where y[1] = "blah")
You'll need to deserialize the JSON into a non-array object graph, and then copy the properties into arrays. I'm not aware of any shortcut for it.
The basic loop once you've deserialized the JSON into obj is roughly:
var ar;
var key;
ar = [];
for (key in obj) {
if (obj.hasOwnProperty(key)) {
ar[key] = obj[key];
}
}
...except you'll have to detect that obj[key] is an object and recurse, which I'll leave as an exercise for the reader.
Note that in JavaScript, arrays aren't really arrays. Depending on your use-case, you may not need to do the conversion from object to Array at all.
I'm pretty sure that in Javascript, you can access this:
x = {
"1" : "a",
"7" : "something",
"3" : {
"1" : "blah"
}
}
Like this:
alert( x["1"] );
which should alert "a". As people already mentioned in the comments above, an array in JS is an "object" already, and there isn't a difference between accessing them these two different ways.
Edit:
Yeah I just tested it and it works. Also, I tried this:
alert( x[1] );
That is, I tried it with the "number" 1, not the "string" "1". It still worked. Yes it's a very strange programming language.
I think this is pretty close:
function parse_obj(obj)
{
var array = [];
var prop;
for (prop in obj)
{
if (obj.hasOwnProperty(prop))
{
var key = parseInt(prop, 10);
var value = obj[prop];
if (typeof value === "object")
{
value = parse_obj(value);
}
array[key] = value;
}
}
return array;
}
http://jsfiddle.net/shaggyfrog/DFnnm/
This works for me:
var obj = JSON.parse('{"1":"a","7":"something","3":{"1":"blah"}}');
var keys = Object.keys(obj).sort();
// Make obj look like it's an array by taking the highest value
// key and using it to give obj a length property.
obj.length = parseInt( keys[keys.length] ) + 1;
var arr = Array.prototype.slice.call(obj, 0);
arr now looks like this:
[undefined, "a", undefined, Object, undefined, undefined, undefined, "something"]
Ok, so it hasn't turned the 'inner' JSON object into an array, but a quick recursive loop doing the same thing as above should sort that out.
Related
I am trying to deep-clone an object, say "a" with k = JSON.parse(JSON.stringify(a)). It is important that I use the stringify way, since I am trying to save the object into a file and then load from it.
I stumbled upon a problem with references on the cloned object which is illustrated below:
var obj={};
obj.importantProperty={s:2};
obj.c=obj.importantProperty;
obj.d=obj.importantProperty;
console.log( obj.c === obj.d ); // Returns true
var cloned = JSON.parse(JSON.stringify(obj));
console.log( cloned.c === cloned.d ); // Returns false
I need the references to be kept when using JSON.parse, in the above example they are not. In my project the object is much more complicated, but in the end it comes down to the example above.
Thanks in advance to anyone who helps me with this :)
The proper way to do something like this would be to store the common referenced object(s) separately and reference it by an ID.
For instance, you can hold your importantProperty objects in an array and use the index as the ID:
var importantProperties = [
{ s: 1 },
{ s: 2 },
{ s: 3 }
];
var obj = {};
obj.importantProperty = importantProperties[1];
obj.c = obj.importantProperty;
obj.d = obj.importantProperty;
Then when you stringify the object you replace the referenced object with its index:
var stringified = JSON.stringify(obj, function(key, value) {
if (key) {
return importantProperties.indexOf(value);
}
return value;
});
console.log(stringified);
// prints {"importantProperty":1,"c":1,"d":1}
And then when you parse you simply reverse the process to revive the references:
var parsed = JSON.parse(stringified, function(key, value) {
if (key) {
return importantProperties[value];
}
return value;
});
console.log(parsed.c === parsed.d && parsed.d === parsed.importantProperty);
// prints true
Now, the example above works for your example code under the assumption that all properties in obj is an object from the importantProperties array. If that's not the case and it's only certain properties that is an importantProperties object, you need to check for that when replacing/reviving.
Assuming only the "importantProperty", "c" and "d" properties are such objects:
if (['importantProperty', 'c', 'd'].includes(key)) instead of just if (key)
If this isn't good enough and you don't want the property name to have anything to do with whether or not the value is an importantProperties object, you'll need to indicate this in the value together with the identifier. Here's an example of how this can be done:
// Replacing
JSON.stringify(obj, function(k, value) {
if (importantProperties.includes(value)) {
return 'ImportantProperty['
+ importantProperties.indexOf(value)
+ ']';
}
return value;
});
// Reviving
JSON.parse(stringified, function(k, value) {
if (/^ImportantProperty\[\d+\]$/.test(value)) {
var index = Number( value.match(/\d+/)[0] );
return importantProperties[index];
}
return value;
});
It is impossible to achieve your desired result using JSON because JSON format can contain only a limited ammount of data types (http://json.org/) and when you stringify an object to JSON some information gets lost.
Probably there is some other kind of serialization technique, but I would recommend you to look for another approach to store data.
I've got a object like this:
{"status":200,
"success":true,
"result": [ {"Description":"", "Year":"", "Price/STK":"", "Main Cat":"Fruits"} ]
}
I have distinct lists I need to use, and the Price key can be: Price/STK, Price/Box, Price/Btl or Price.
I know I can get the value using, for example, data.result['Price/STK'], but I don't want to check every key, I'd like to search for the price and just use.
How would I determine if a word ('Price*', for example) is part of a key and get that value?
There's no built in way to do this, you have to iterate and check each key.
You could just create a convenient function :
function matchKey(objectToSearch, keyToFind) {
for (var k in objectToSearch) {
if ( k.toLowerCase().indexOf(keyToFind.toLowerCase()) !== -1)
return objectToSearch[k];
}
return null;
}
matchKey({year : 2015, "Price/STK" : "value"}, "price"); // returns "value"
FIDDLE
You could solve this problem easily using lodash (or underscore)
_.findKey(obj, function(key) { return _.startsWith(key, 'Price')})
This finds the first key that starts with price.
You can get the property names of an object using Object.keys, and then use indexOf to search for a value, but it does an exact match and doesn't take a regular expression as an argument.
So you have to loop over all the property names until you find the one you want. There are built–in iterators to help:
var obj = {"status":200,
"success":true,
"result": [ {"Description":"desc",
"Year":"yr",
"Price/STK":"price/stk",
"Main Cat":"Fruits"}
]
};
function getValueLike(obj, prop){
var re = new RegExp('^' + prop);
var value;
Object.keys(obj).some(function(prop) {
if (re.test(prop)) {
value = obj[prop];
return true;
}
});
return value;
}
document.write(getValueLike(obj.result[0], 'Price')); // price/stk
A version that uses indexOf on the property name might be faster and is a little less code:
function getValueLike(obj, prop){
var value;
Object.keys(obj).some(function(key) {
if (key.indexOf(prop) == 0) {
value = obj[key];
return true;
}
});
return value;
}
which can be reduced to:
function getValueLike(obj, prop, value){
Object.keys(obj).some(function(key) {return key.indexOf(prop) == 0 && ((value = obj[key]) || true)});
return value;
}
which also allows a default value to be passed to value, but it's a little too obfuscated for me.
Using an arrow function:
function getValueLike(obj, prop, value){
Object.keys(obj).some(key => key.indexOf(prop) == 0 && ((value = obj[key]) || true));
return value;
}
Filter the set of keys on the result array's object for "Price", and then return the value associated with that. I made a function for it as an example.
function selectPrice(obj){
return obj.result[0][
Object.keys(obj.result[0]).filter(function(el){
return el.indexOf("Price") > -1
})[0]
]
}
var data = {"status":200,
"success":true,
"result": [ {"Description":"", "Year":"", "Price/STK":"6", "Main Cat":"Fruits"} ]
};
document.write(selectPrice(data));
I wanted to write one version of a function that iterated over both Array objects and Objects, with minimal duplicate code. Something like:
function (arr_or_obj) {
arr_or_obj.forEach( function(key,value) {
// do stuff with key and value
});
}
This was before I realized that (in Chrome at least), Object.keys returns a list of the keys for an array. So I could use that to treat the Array like an object. Like so:
function (arr_or_obj) {
var keys = Object.keys(arr_or_obj);
keys.forEach( function(key) {
var value = arr_or_obj[key];
// do stuff with key and value
});
}
Problem solved. But this was not before I wrote my own "JavaScript pseudo-Array iterator". The only advantage of this was that instead of getting a list of keys for the Array (which could be very long), we save memory by producing only the return values we need. Like so:
function create_iterator(max) {
var jkeys = {};
jkeys.count_ = 0;
jkeys.length = max;
jkeys.getter_ = function() {
var returnable = jkeys.count_;
jkeys.count_ += 1;
jkeys.__defineGetter__(jkeys.count_,jkeys.getter_);
delete jkeys[jkeys.count_-1];
return returnable;
};
jkeys.__defineGetter__(0, jkeys.getter_);
return jkeys;
}
Which you can then call by going:
var z = create_iterator(100);
z[0];
>> 0
z[0];
>> undefined
z[1];
>> 1;
z[2]
>> 2;
...
This is sort of a question and answer in one, but the obvious question is, is there a better way to do this without using Object.keys?
Object.keys gives you an array of the object's own enumerable properties. That is, properties it has itself, not from its prototype, and that are enumerable (so not including things like length on arrays, which is a non-enumerable property).
You can do the same thing with for-in if you use correct safeguards:
var key;
for (key in arr_or_obj) {
if (arr_or_obj.hasOwnProperty(key)) {
// do something with it
}
}
Now you're doing the same thing you were doing with Object.keys.
But, note that this (and Object.keys) will include properties people put on arrays that aren't array indexes (something which is perfectly valid in JavaScript). E.g.:
var key;
var a = [1, 2, 3];
a.foo = "bar";
for (key in a) {
if (a.hasOwnProperty(key)) {
console.log(key); // Will show "foo" as well as "0", "1", and "2"
}
}
If you want to only process array indexes and not other properties, you'll need to know whether the thing is an array, and then if so use the "is this an index" test:
var key;
var isArray = Array.isArray(arr_or_obj);
// Or (see below):
//var isArray = Object.prototype.toString.call(arr_or_obj) === "[object Array]";
for (key in arr_or_obj) {
if (a.hasOwnProperty(key) &&
(!isArray || isArrayIndex(key))
) {
console.log(key); // Will only show "0", "1", and "2" for `a` above
}
}
...where Array.isArray is a new feature of ES5 which is easily shimmed for older browsers if necessary:
if (!Array.isArray) {
Array.isArray = (function() {
var toString = Object.prototype.toString;
function isArray(arg) {
return toString.call(arg) === "[object Array]";
}
return isArray;
})();
}
...and isArrayIndex is:
function isArrayIndex(key) {
return /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294; // 2^32 - 2
}
See this other answer for details on that (where the magic numbers come from, etc.).
That loop above will loop through an object's own enumerable properties (all of them) if it's not an array, and loop through the indexes (but not other properties) if it's an array.
Taking it a step further, what if you want to include the properties the object gets from its prototype? Object.keys omits those (by design). If you want to include them, just don't use hasOwnProperty in the for-in loop.
You can try something like this...
Live Demo
function forIn(obj) {
var isArray = Array.isArray(obj),
temp = isArray ? obj : Object.keys(obj);
temp.forEach(function (value, index, array) {
console.log(this[isArray ? index : value]);
}, obj);
}
You can write:
for (key in arr_or_obj) {
if (arr_or_obj.hasOwnProperty(key) {
value = arr_or_obj[key];
// do stuff with key and value
}
}
I have this code:
var showRegion = function(key) {
if (key in regionOptions) {
var entry = regionOptions[key];
var builder = entry.builder;
var layoutObj = entry.layoutObj;
var viewStarter = entry.viewStarter;
var view = new builder();
logger.info('Controller.' + key + ' => CreateAccountLayoutController');
Controller.layout[layoutObj].show(view);
view[viewStarter]();
}
};
What I need is that the parameter should be able to accept an array or a string, and should work either way.
Sample function calls:
showRegion('phoneNumberRegion');
showRegion(['phoneNumberRegion', 'keyboardRegion', 'nextRegion']);
This post is old, but here is a pretty good tip:
function showRegions(keys) {
keys = [].concat(keys)
return keys
}
// short way
const showRegions = key => [].concat(keys)
showRegions(1) // [1]
showRegions([1, 2, 3]) // [1, 2, 3]
var showRegion = function(key) {
if (typeof key === 'string')
key = [key];
if (key in regionOptions) {
...
No need to make a code for each case, just convert key string into an array of one element and the code for arrays will do for both.
you could use typeof to check for the type of your argument and proceed accordingly, like
var showRegion = function(key) {
if( typeof key === 'object') {
//its an object
}
else {
//not object
}
}
You can use the fact that string.toString() always returns the same string and Array.toString() returns a comma-delimited string in combination with string.split(',') to accept three possible inputs: a string, an array, a comma-delimited string -- and reliably convert to an array (provided that you're not expecting commas to be part of the values themselves, and you don't mind numbers becoming strings).
In the simplest sense:
x.toString().split(',');
So that
'a' -> ['a']
['a','b'] -> ['a','b']
'a,b,c' -> ['a','b','c']
1 -> ['1']
Ideally, you may want to tolerate null, undefined, empty-string, empty-array (and still keep a convenient one-liner):
( (x || x === 0 ) && ( x.length || x === parseFloat(x) ) ? x.toString().split(',') : []);
So that also
null|undefined -> []
0 -> ['0']
[] -> []
'' -> []
You may want to interpret null/empty/undefined differently, but for consistency, this method converts those to an empty array, so that downstream code does not have to check beyond array-having-elements (or if iterating, no check necessary.)
This may not be terribly performant, if that's a constraint for you.
In your usage:
var showRegion = function(key) {
key = ( (key || key === 0 ) && ( key.length || key === parseFloat(key) ) ? key.toString().split(',') : []);
/* do work assuming key is an array of strings, or an empty array */
}
There seems to have many question asked similar on counting number of element already but I am failing to implement them with mine problem.
After jquery ajax I get JSON data returned which looks something like this
Object {0: Object, 1: Object , xxxx:"asdf" ,yyyy:"asdf", zzzz:"asdf"}
I want to get number of object between this { } braces ( not counting those xxx,yyy element )
I tried .length which doesn't work
I also tried using this Length of a JavaScript object but that return the number of element in each object. I just want the number of object
Thank You
Try this:
var json = { 0: {}, 1: {}, xxxx: "asdf", yyyy: "asdf", zzzz: "asdf" };
function typeOf( obj ) {
return ({}).toString.call( obj )
.match(/\s([a-zA-Z]+)/)[1].toLowerCase();
}
var total = 0;
for ( var o in json ) {
if ( typeOf( json[o] ) == 'object' ) {
total++;
}
}
console.log( total ); //=> 2
Everything is an object in JavaScript. The typeof operator is misleading and won't work in this case. You can use the typeOf function above that I extracted from this blog post: Fixing the JavaScript typeof operator (worth reading). There are other ways of doing it but this seems like the most straightforward.
If it's not just a coincidence that the objects are the ones with numeric property names, and the numeric properties count up sequentially, you could do something like this:
var obj = { /* your object here */ }
for (var i=0; i in obj; i++) {
// use obj[i] for something
}
// i is now equal to the number of numeric properties
This works because as soon as i is high enough that you've run out of properties the in operator will return false. Feel free to use .hasOwnProperty() instead if you prefer.
Obviously this is a specialised solution that doesn't test the type of the different properties at all. (To actually test the type see elclanrs' solution - and either way read the page he linked to.)
Say that the entire json is in a variable called json:
var total_objects = 0;
$.each(json, function () {
if (typeof this == 'object') {
total_objects++;
}
});
However, I am curious as to why you would need to know this.
You can use a customized version from the code of this question Length of Javascript Object (ie. Associative Array) and check for element's type using typeof operator and count only those which are an object (or an array).
Object.sizeObj = function(obj) {
var size = 0, key;
for (key in obj) {
if (typeof key[obj] === 'object' && obj.hasOwnProperty(key)) size++;
}
return size;
};
// Get the count of those elements which are an object
var objectsCount = Object.sizeObj(myArray);