is there a data structure or a pattern in Javascript that can be used for both fast lookup (by key, as with associative arrays) and for ordered looping?
Right, now I am using object literals to store my data but I just disovered that Chrome does not maintain the order when looping over the property names.
Is there a common way to solve this in Javascript?
Thanks for any hints.
Create a data structure yourselves. Store the ordering in an array that is internal to the structure. Store the objects mapped by a key in a regular object. Let's call it OrderedMap which will have a map, an array, and four basic methods.
OrderedMap
map
_array
set(key, value)
get(key)
remove(key)
forEach(fn)
function OrderedMap() {
this.map = {};
this._array = [];
}
When inserting an element, add it to the array at the desired position as well as to the object. Insertion by index or at the end is in O(1).
OrderedMap.prototype.set = function(key, value) {
// key already exists, replace value
if(key in this.map) {
this.map[key] = value;
}
// insert new key and value
else {
this._array.push(key);
this.map[key] = value;
}
};
When deleting an object, remove it from the array and the object. If deleting by a key or a value, complexity is O(n) since you will need to traverse the internal array that maintains ordering. When deleting by index, complexity is O(1) since you have direct access to the value in both the array and the object.
OrderedMap.prototype.remove = function(key) {
var index = this._array.indexOf(key);
if(index == -1) {
throw new Error('key does not exist');
}
this._array.splice(index, 1);
delete this.map[key];
};
Lookups will be in O(1). Retrieve the value by key from the associative array (object).
OrderedMap.prototype.get = function(key) {
return this.map[key];
};
Traversal will be ordered and can use either of the approaches. When ordered traversal is required, create an array with the objects (values only) and return it. Being an array, it would not support keyed access. The other option is to ask the client to provide a callback function that should be applied to each object in the array.
OrderedMap.prototype.forEach = function(f) {
var key, value;
for(var i = 0; i < this._array.length; i++) {
key = this._array[i];
value = this.map[key];
f(key, value);
}
};
See Google's implementation of a LinkedMap from the Closure Library for documentation and source for such a class.
The only instance in which Chrome doesn't maintain the order of keys in an object literal seems to be if the keys are numeric.
var properties = ["damsonplum", "9", "banana", "1", "apple", "cherry", "342"];
var objLiteral = {
damsonplum: new Date(),
"9": "nine",
banana: [1,2,3],
"1": "one",
apple: /.*/,
cherry: {a: 3, b: true},
"342": "three hundred forty-two"
}
function load() {
var literalKeyOrder = [];
for (var key in objLiteral) {
literalKeyOrder.push(key);
}
var incremental = {};
for (var i = 0, prop; prop = properties[i]; i++) {
incremental[prop] = objLiteral[prop];
}
var incrementalKeyOrder = [];
for (var key in incremental) {
incrementalKeyOrder.push(key);
}
alert("Expected order: " + properties.join() +
"\nKey order (literal): " + literalKeyOrder.join() +
"\nKey order (incremental): " + incrementalKeyOrder.join());
}
In Chrome, the above produces: "1,9,342,damsonplum,banana,apple,cherry".
In other browsers, it produces "damsonplum,9,banana,1,apple,cherry,342".
So unless your keys are numeric, I think even in Chrome, you're safe. And if your keys are numeric, maybe just prepend them with a string.
As
has been noted, if your keys are numeric
you can prepend them with a string to preserve order.
var qy = {
_141: '256k AAC',
_22: '720p H.264 192k AAC',
_84: '720p 3D 192k AAC',
_140: '128k AAC'
};
Example
Related
i want to push an object to an array(last called objects) and store this array to localstorge. this array fills every call with new objects. If an objects still exist in the array, the older one will be replaced.
My code so far:
function pushToStorage(groupId, objectId, groupIcon, displayString) {
var objKey = "object_" + groupId + "_" + objectId;
var objects = storage.get("objects");
if (objects) {
console.log($objects);
} else {
objects = [];
}
var object = {
groupid: groupId,
objectid: objectId,
groupicon: groupIcon,
display: displayString
};
objects[objKey] = object;
console.log(objects);
storage.set("objects", objects);
}
i use this jquery plugin jstorage
im not an js pro and at the moment, only one object get stored correct.
So my questions:
How to store an array of objects to local storage, get it back, and add new objects to this array
How to manage that there is only one unique object in this array
How to limit the array by eg the 50 newest and kick the older ones
thx for any suggestions or snippets
EDIT: some people mark this as duplicate - but the linked answer is only a part of my. I read this before but my problem is to set/get an array with unique objects. i think it is more complex.
in your case objects = [] will fail to store it to localStorage change it to objects = {}.
test it
var objects = [];
objects['objkey'] = {red:'#FF0000'}
var json_str = JSON.stringify(test);
console.log(json_str)
// []
for point 1 and 2, since it using object key name there will be no duplicate, it will be overwritten with new value, no other action needed.
for point 3, if you do objects[objKey] = object; it will append object to last position so oldest position to delete is index 0
function pushToStorage(groupId, objectId, groupIcon, displayString) {
var objKey = "object_" + groupId + "_" + objectId;
var objects = storage.get("objects");
if(objects !== null) {
console.log(objects);
// if not objKey and length objects more than 50 delete oldest
if(storage.isSet(objects[objKey]) === false && Object.keys(objects).length == 50){
// delete oldest object
delete objects[0];
}
}
else {
objects = {};
}
var object = {
groupid: groupId,
objectid: objectId,
groupicon: groupIcon,
display: displayString
};
objects[objKey] = object;
//console.log(objects);
storage.set("objects", objects);
}
update
I have adjusted/corrected the example objects, because they contained an error before.
I have an mapping object that looks like this:
var AtoB = {
"amore" : "love",
"alimenti": "food",
"Bier" : "beer"
};
which allows to map one way i.e. AtoB["love"] yields "amore". I could add an inverse to it manualy i.e.
var BtoA = {
"love": "amore",
"food": "alimenti",
"beer": "Bier"
};
Anyway it troublesome the two objects in sync and I would like to create the BtoA programmatically in Javascript. Is there some sort of function xyz() which yields var BtoA = xyz(AtoB);?
The example above can be extended to include a problem (e.g. if I have too much "beer")
var AtoB = {
"amore" : "love",
"alimenti": "food",
"Bier" : "beer"
"cerveza" : "beer",
"pivo" : "beer",
"birra" : "beer",
"cerveja" : "beer"
};
as this is not a 1-to-1 mapping. In amateuer math terms It is not an inversible function?
To make things even more complicated I have a recipe for desaster.
var makeStuff = {
"agriculture": "food",
"hops" : {
"water": {
"malt": "beer"},
"malt": {
"water": "beer"}},
"water" : {
"hops": {
"malt": "beer"},
"malt": {
"hops": "beer"}},
"malt" : {
"water": {
"hops": "beer"},
"hops": {
"water": "beer"}}
};
inversing this nested javascript object, seems even more challanging for such an xyz() function. Anyway maybe there is such an xyz() function, then I would be glad to accept this as an answer to this question
Very simple. Following is the code to inverse key, value.
var inverse= (function inv(param){
for(var attr in param) {
if(param.hasOwnProperty(attr)) {
if(typeof param[attr]==="string") {
param[param[attr]] = attr;
delete param[attr];
} else if (Object.prototype.toString.call(param[attr]) === "[object Object]") {
param[attr] = inv(param[attr]);
}
}
}
return param;
});
To get the result into other object, initialize it with empty and assign it. Like
var BtoA = {};
BtoA = inverse(AtoB);
And, The JSON:
var AtoB = {
"love": "amore",
"food": "alimenti",
"beer": "Bier",
"beer": "cerveza",
"beer": "pivo",
"beer": "birra",
"beer": "cerveja",
};
has only three attributes because JSON is a dictionary data structure: new key will replace the old one. So the above JSON will actually be like:
{love: "amore", food: "alimenti", beer: "cerveja"}
So, inverting the above given JSON (AtoB) will result in the inversion of only three properties, and final result will be:
{amore: "love", alimenti: "food", cerveja: "beer"}
The answer from Muhammad imran is effective if the purpose/target is a simple inversion (i.e. no nested object structure; no multiple values).
Obviously that is the best result to be achieved, if no further artifice is created, to cover the fact that the key->value relation in objects are:
keys are unique,
values can be muliple.
Looking at the beer example above it is somewhat regretible that the information is lost in the inversion. Therefore this answer should supplement and enrich and provide a way in which the information can be stored. The way to achieve it is using Javascript Arrays within the resulting inverted object, to allow to store the potentially ambigious new values. as for example.
var BeerAtoB = {
"amore" : "love",
"alimenti": "food",
"Bier" : "beer",
"cerveza" : "beer",
"pivo" : "beer",
"birra" : "beer",
"cerveja" : "beer"
};
allowing to translate (de,es,pl/cz,it,pt)"beer" to English would best store
this information in the inverted too
var BeerBtoA = {
"love" : "amore",
"food" : "alimenti",
"beer" : [ "Bier" ,
"cerveza",
"pivo",
"birra",
"cerveja"
]
};
a version in which less information get lost and the multipleness of the original value "beer" is reflected by multipleness of values under the joint, inverted key "beer" now.
To accomplish this I made an enhanced inverting function
function invertObject(obj)
{
var invertedObject = {};
// make a stack and prime it with the obj
var stack = [];
stack.push({"way":[],"obj":obj});
// while stuff on the stack
while (stack.length)
{
var way= stack[0].way;
var obj= stack[0].obj;
for (var prop in obj)
{
if (typeof obj[prop] === 'object')
{
// attributes, which are themselves objects are added to the stack,
// with their way information.
stack.push({"way":way.concat(prop),"obj":obj[prop]});
}
else
{
// always start with adding things to the invertedObject,
var curobj = invertedObject;
var value = newKey = obj[prop];
var curpath = way.concat(prop).concat(obj[prop]);
// for all but the last two path elements the loop below
// will create the inverted path, starting with the value (obj[prop])
// as key, Since values need not be unique (as keys), create each
// such new key-property as an Array, not to loose inverted pathes.
while(curpath.length>2)
{
var pathpart = curpath.pop();
if(!curobj.hasOwnProperty(pathpart))
{
curobj[pathpart]=[];
}
curobj=curobj[pathpart];
}
// the last two curpath Array members represent the last key and the
// new to be added value.
var preLastPart = curpath.pop();
var lastPart = curpath.pop();
// Again the artifice of an Array is used since
// the inverted keys are not unique, hence cases in which
// 1 key has (>1) values.
if(!curobj.hasOwnProperty(preLastPart))
{
curobj[preLastPart]=[];
}
curobj[preLastPart].push(lastPart);
}
}
stack.shift();
}
return invertedObject; function invertObject(obj)
{
var invertedObject = {};
// make a stack and prime it with the obj
var stack = [];
stack.push({"way":[],"obj":obj});
// while stuff on the stack
while (stack.length)
{
var way= stack[0].way;
var obj= stack[0].obj;
for (var prop in obj)
{
if (typeof obj[prop] === 'object')
{
// attributes, which are themselves objects are added to the stack,
// with their way information.
stack.push({"way":way.concat(prop),"obj":obj[prop]});
}
else
{
// always start with adding things to the invertedObject,
var curobj = invertedObject;
var value = newKey = obj[prop];
var curpath = way.concat(prop).concat(obj[prop]);
// for all but the last two path elements the loop below
// will create the inverted path, starting with the value (obj[prop])
// as key, Since values need not be unique (as keys), create each
// such new key-property as an Array, not to loose inverted pathes.
while(curpath.length>2)
{
var pathpart = curpath.pop();
if(!curobj.hasOwnProperty(pathpart))
{
curobj[pathpart]=[];
}
curobj=curobj[pathpart];
}
// the last two curpath Array members represent the last key and the
// new to be added value.
var preLastPart = curpath.pop();
var lastPart = curpath.pop();
// Again the artifice of an Array is used since
// the inverted keys are not unique, hence cases in which
// 1 key has (>1) values.
if(!curobj.hasOwnProperty(preLastPart))
{
curobj[preLastPart]=[];
}
curobj[preLastPart].push(lastPart);
}
}
stack.shift();
}
return invertedObject;
}
}
Indeed since equal values can be found in many places of simple and nested object, the result will be an object in which each value will be an Array for two reasons:
Several original object's keys, can have the same value, and therefore (after inverting) more than one value can exists. An Array can store all those multiple new values, hence all information.
While in an nested object, the uniqueness makes every property either a direct value or a subobject, in the inverted object at a key, we can find not only muliple values, but also that at the very same place there are also further nested objects. (For this reason it is lucky that an Javascript Array, as being an Object, does besides its entry allow also for further properties to be attached to it and hence can serve simultaneously as a storage for the multiple values and as a subkey in the nested structure. Such a double purpose of Arrays in the inverted object structure, is unforunatelly hard to show in JSON notation, as the JSON notation does not allow for Arrays with Object Attributes)
how can I sort object properties by name using another array as refer?
var json = '{"b":90,"c":42, "a":34}';
var obj = JSON.parse(json);
var sorting = ["a","b","c"];
I would like to have obj properties ordered just like sorting array
Thank you
Bye
var sorting = Object.keys(obj).sort();
Javascript objects are not ordered. So, you cannot actually sort them.
Why not just iterating over the array, and then access obj properties ?
for ( var i = 0; i < sorting.length; ++i ) {
var current = obj[ sorting[ i ] ];
// other stuff here...
}
If you don't intent to iterate over the obj, please explain your actual needs.
Convert Object to Array
var jsonArray = [];
$.each(myObj, function(i,n) {
jsonArray.push(n);
});
Sort Array
var jsonArraySort = jsonArray.sort();
Convert Array to Object
var jsonSort = {};
for(var i = 0; i < jsonArraySort.length; i++) {
jsonSort[i] = jsonArraySort[i];
}
You could try something like:
var sorted = []
for (i = 0; i < sorting.length; i++) {
if (json.hasOwnProperty(sorting[i])) {
sorted.push(json[sorting[i]);
}
}
/* sorted will equal [34, 90, 42] */
You cannot order the keys of an object, as per definition,
An ECMAScript object is an unordered collection of propertiesES3 Specs
The mechanics and order of enumerating the properties (step 6.a in the first algorithm, step 7.a in the second) is not specified.
Properties of the object being enumerated may be deleted during enumeration. If a property that has not yet been visited during enumeration is deleted, then it will not be visited. If new properties are added to the object being enumerated during enumeration, the newly added properties are not guaranteed to be visited in the active enumeration. A property name must not be visited more than once in any enumeration.ES5 Specs
If you want to a sorted array consisting of your objects keys, you can use [Object.keys][4], To get an array of them, which you can then sort.
var obj = {b:90,c:42, a:34}
console.log (
Object.keys (obj).sort ()
) // ["a","b","c"]
If you are interested in a sorted array, containing both, keys and values, you could do the following.
var obj = {b: 90, c: 42, a: 34},
srt = Object.keys(obj).sort().map(function (prop) {
return {
key: prop,
value: obj[prop]
}
});
console.log(srt) //[{"key":"a","value":34},{"key":"b","value":90},{"key":"c","value":42}]
I have some problem with sorting items inside object. So I have something like this:
var someObject = {
'type1': 'abc',
'type2': 'gty',
'type3': 'qwe',
'type4': 'bbvdd',
'type5': 'zxczvdf'
};
I want to sort someObject by value, and this is where I have problem.
I have sorting function that should return key/value pairs sorted by value:
function SortObject(passedObject) {
var values = [];
var sorted_obj = {};
for (var key in passedObject) {
if (passedObject.hasOwnProperty(key)) {
values.push(passedObject[key]);
}
}
// sort keys
values.sort();
// create new object based on Sorted Keys
jQuery.each(values, function (i, value) {
var key = GetKey(passedObject, value);
sorted_obj[key] = value;
});
return sorted_obj;
}
and function to get key:
function GetKey(someObject, value) {
for (var key in someObject) {
if (someObject[key] === value) {
return key;
}
}
}
The problem is in last part when creating new, returning object - it's sorted by key again. Why? And this is specific situation when i have to operate on object NOT on array (yes I know that would be easier...)
Does anyone know how to sort items in object?
Plain objects don't have order at all. Arrays -that are a special types of objects- have.
The most close thing that you can have is an array with the object values sorted . Something like, for example:
_valuesOfAnObjectSorted = Object.keys(object).map(function(k){ return object[k]; }).sort();
You have two possibilities:
Refactor your object into an array
Something like this:
var myObj = [
['type1', 'abc'],
['type2', 'gty'],
...
];
Or even better, since using it somewhere would not rely on array positions but full named keys:
var myObj = [
{name: 'type1', val:'abc'},
{name: 'type2', val:'gty'},
...
];
Use your object with an auxiliar array
Wherever you want to use your object ordered by the keys, you can extract the keys as an array, order it and traverse it to access the object
var ordKeys = Object.keys(myObj).sort(); // pass inside a function if you want specific order
var key;
for (var i = 0, len = ordKeys.length; i < len; i +=1) {
key = ordKeys[i]
alert(key + " - " + myObj[key]);
}
Combination of both of them
If the object is not constructed by you, but comes from somewhere else, you can use the second option approach to construct an array of objects as in the first option. That would let you use your array anywhere with perfect order.
EDIT
You might want to check the library underscore.js. There you have extremely useful methods that could do the trick pretty easily. Probably the method _.pairs with some mapping would do all the work in one statement.
I need to create object in javascript that allows to fetch values using keys & also iterate over keys. The primary requirement is fetching value by key but iteration is required to maintain sort order of entries by values(integer).
How do I go about creating such an object ?
sampleJson={
"1":"john",
"2":"johny"
}
You can iterate using for in loop
for(key in sampleJson){
//ur code
}
All objects in JavaScript are JSONeable! (is that really a word).
All objects in JavaScript are a collection of key value mappings.
A for in loop iterates over the keys of an object.
Native objects don't support exactly what you're looking for, but it's fairly straightforward to create a wrapper around native objects that provides extra functionality.
A possible approach:
function KeySortArr(obj) {
this.obj = obj || {};
}
KeySortArr.prototype = {
get: function(key) {
return this.obj[key];
},
set: function(key, value) {
this.obj[key] = value;
},
keys: function() {
var keys = [];
for(var i in this.obj) {
if (this.obj.hasOwnProperty(i))
keys.push(i);
}
return keys;
},
getKeysSortedByValue: function() {
var obj = this.obj;
return this.keys().sort(function(a, b) {
return obj[a] > obj[b];
});
}
};
Usage:
var arr = new KeySortArr();
arr.set("test", 4);
arr.set("another", 2);
arr.set("ok", 60);
arr.set("last", 14);
The following then:
arr.getKeysSortedByValue();
Returns:
["another, "test", "last", "ok"]
In other words, the keys are sorted by their associated value. This might not be exactly what you were looking for, but it should be close.