Is array both associative and indexed? - javascript

Can an array in JavaScript be associative AND indexed?
I'd like to be able to lookup an item in the array by its position or a key value.

There are no such things as associative arrays in Javascript. You can use object literals, which look like associative arrays, but they have unordered properties. Regular Javascript arrays are based on integer indexes, and can't be associative.
For example, with this object:
var params = {
foo: 1,
bar: 0,
other: 2
};
You can access properties from the object, for example:
params["foo"];
And you can also iterate over the object using the for...in statement:
for(var v in params) {
//v is equal to the currently iterated property
}
However, there is no strict rule on the order of property iteration - two iterations of your object literal could return the properties in different orders.

After reading the Wikipedia definition of associative array, I'm going to break with traditional JavaScript lore and say, "yes, JavaScript does have associative arrays." With JavaScript arrays, you can add, reassign, remove, and lookup values by their keys (and the keys can be quoted strings), which is what Wikipedia says associative arrays should be able to do.
However, you seem to be asking something different--whether you can look up the same value by either index or key. That's not a requirement of associative arrays (see the Wikipedia article.) Associative arrays don't have to give you the ability to get a value by index.
JavaScript arrays are very closely akin to JavaScript objects.
arr=[];
arr[0]="zero";
arr[1]="one";
arr[2]="two";
arr["fancy"]="what?";
Yes, that's an array, and yes, you can get away with non-numeric indices. (If you're curious, after all this, arr.length is 3.)
In most cases, I think you should stick to numeric indices when you use arrays. That what most programmers expect, I think.
The link is to my blog post about the subject.

Native JS objects only accept strings as property names, which is true even for numeric array indices; arrays differ from vanilla objects only insofar as most JS implementations will store numerically indexed properties differently (ie in an actual array as long as they are dense) and setting them will trigger additional operations (eg adjustment of the length property).
If you're looking for a map which accepts arbitrary keys, you'll have to use a non-native implementation. The script is intended for fast iteration and not random-access by numeric indices, so it might nor be what you're looking for.
A barebones implementation of a map which would do what you're asking for could look like this:
function Map() {
this.length = 0;
this.store = {};
}
Map.prototype.get = function(key) {
return this.store.hasOwnProperty(key) ?
this.store[key] : undefined;
};
Map.prototype.put = function(key, value, index) {
if(arguments.length < 3) {
if(this.store.hasOwnProperty(key)) {
this.store[key].value = value;
return this;
}
index = this.length;
}
else if(index >>> 0 !== index || index >= 0xffffffff)
throw new Error('illegal index argument');
if(index >= this.length)
this.length = index + 1;
this[index] = this.store[key] =
{ index : index, key : key, value : value };
return this;
};
The index argument of put() is optional.
You can access the values in a map map either by key or index via
map.get('key').value
map[2].value

var myArray = Array();
myArray["first"] = "Object1";
myArray["second"] = "Object2";
myArray["third"] = "Object3";
Object.keys(myArray); // returns ["first", "second", "third"]
Object.keys(myArray).length; // returns 3
if you want the first element then you can use it like so:
myArray[Object.keys(myArray)[0]]; // returns "Object1"

The order in which objects appear in an associative javascript array is not defined, and will differ across different implementations. For that reason you can't really count on a given associative key to always be at the same index.
EDIT:
as Perspx points out, there aren't really true associative arrays in javascript. The statement foo["bar"] is just syntactic sugar for foo.bar
If you trust the browser to maintain the order of elements in an object, you could write a function
function valueForIndex(obj, index) {
var i = 0;
for (var key in obj) {
if (i++ == index)
return obj[key];
}
}

var stuff = [];
stuff[0] = "foo";
stuff.bar = stuff[0]; // stuff.bar can be stuff["bar"] if you prefer
var key = "bar";
alert(stuff[0] + ", " + stuff[key]); // shows "foo, foo"

I came here to wanting to know if this is bad practice or not, and instead found a lot of people appearing not to understand the question.
I wanted to have a data structure that was ordered but could be indexed by key, so that it wouldn't require iteration for every lookup.
In practical terms this is quite simple, but I still haven't read anything on whether it's a terrible practice or not.
var roygbiv = [];
var colour = { key : "red", hex : "#FF0000" };
roygbiv.push(colour);
roygbiv[colour.key] = colour;
...
console.log("Hex colours of the rainbow in order:");
for (var i = 0; i < roygbiv.length; i++) {
console.log(roygbiv[i].key + " is " + roygbiv[i].hex);
}
// input = "red";
console.log("Hex code of input colour:");
console.log(roygbiv[input].hex);
The important thing is to never change the value of array[index] or array[key] directly once the object is set up or the values will no longer match. If the array contains objects you can change the properties of those objects and you will be able to access the changed properties by either method.

Although I agree with the answers given you can actually accomplish what you are saying with getters and setters. For example:
var a = [1];
//This makes a["blah"] refer to a[0]
a.__defineGetter__("blah", function(){return this[0]});
//This makes a["blah"] = 5 actually store 5 into a[0]
a.__defineSetter__("blah", function(val){ this[0] = val});
alert(a["blah"]); // emits 1
a["blah"] = 5;
alert(a[0]); // emits 5
Is this what you are looking for? i think theres a different more modern way to do getters and setters but cant remember.

The tide has changed on this one. Now you can do that... and MORE! Using Harmony Proxies you could definitely solve this problem in many ways.
You'll have to verify that your targeted environments support this with maybe a little help from the harmony-reflect shim.
There's a really good example on the Mozilla Developer Network on using a Proxy to find an array item object by it's property which pretty much sums it up.
Here's my version:
var players = new Proxy(
[{
name: 'monkey',
score: 50
}, {
name: 'giraffe',
score: 100
}, {
name: 'pelican',
score: 150
}], {
get: function(obj, prop) {
if (prop in obj) {
// default behavior
return obj[prop];
}
if (typeof prop == 'string') {
if (prop == 'rank') {
return obj.sort(function(a, b) {
return a.score > b.score ? -1 : 1;
});
}
if (prop == 'revrank') {
return obj.sort(function(a, b) {
return a.score < b.score ? -1 : 1;
});
}
var winner;
var score = 0;
for (var i = 0; i < obj.length; i++) {
var player = obj[i];
if (player.name == prop) {
return player;
} else if (player.score > score) {
score = player.score;
winner = player;
}
}
if (prop == 'winner') {
return winner;
}
return;
}
}
});
console.log(players[0]); // { name: 'monkey', score: 50 }
console.log(players['monkey']); // { name: 'monkey', score: 50 }
console.log(players['zebra']); // undefined
console.log(players.rank); // [ { name: 'pelican', score: 150 },{ name: 'giraffe', score: 100 }, { name: 'monkey', score: 50 } ]
console.log(players.revrank); // [ { name: 'monkey', score: 50 },{ name: 'giraffe', score: 100 },{ name: 'pelican', score: 150 } ]
console.log(players.winner); // { name: 'pelican', score: 150 }

The latest MDN documentation makes it quiet clear that Array index must be integers.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
let arr=[];
arr[0]="zero";
arr[1]="one";
arr[2]="two";
arr["fancy"]="what?";
//Arrays cannot use strings as element indexes (as in an associative array) but must use integers.
//Setting non-integers using bracket notation will not set an element to the Array List itself
//A non-integer will set a variable associated with that ARRAY Object property collection
let denseKeys = [...arr.keys()];
console.log(denseKeys);//[ 0, 1, 2 ]
console.log("ARRAY Keys:"+denseKeys.length);//3
let sparseKeys = Object.keys(arr);
console.log(sparseKeys);//[ '0', '1', '2', 'fancy' ]
console.log("Object Keys:"+sparseKeys.length);//4
const iterator = arr.keys();
for (const key of iterator) {
console.log(key);//0,1,2
}

Yes.
test = new Array();
test[0] = 'yellow';
test['banana'] = 0;
alert(test[test['banana']]);

Related

How can I loop over an array of objects without Object.keys()?

I'm a beginner at JavaScript and I'm trying to solve this problem without Object.keys() or any regex. I have a working solution but I'm wondering if there's a better way to call on the object key within the array while still looping. If anyone has a way to do this that's basic please let me know.
Problem:
Create a function called keyCount which accepts two parameters, an array of objects, and a string. The function should return a number which is the number of times that key appears in the array of objects.
Expected Result:
countTimesOfKey([{name:"Sharon"}, {name: "Manish"},{lastName: "Terma"}], "name")) // 2
My Answer:
function countTimesOfKey(arr, str) {
let count = 0
for (let i in arr){
let test = arr[i]
let test2 = test[str]
if (test2 !== undefined){
count += 1
}
}
return count
}
You can use Array.filter to filter out the items in the array which have str as a property (by using Object.hasOwnProperty), then return the length of the resulting array:
function countTimesOfKey(arr, str) {
return arr.filter(e => e.hasOwnProperty(str)).length;
}
console.log(countTimesOfKey([{
name: "Sharon"
}, {
name: "Manish"
}, {
lastName: "Terma"
}], "name"))
Of course, using Object.keys() and #Spectric's solution are way better than the one below, I just wanted to show that we can even more 'simplify' this.
We can use for...in to
Loop over each object in the array
Loop over each key of the object on the current index
Compare the name of each key against our check variable
Increase our counter
return the result counter
const result = countTimesOfKey([{name:"Sharon"}, {name: "Manish"},{lastName: "Terma"}], "name");
console.log(result);
function countTimesOfKey(arr, name) {
let counter = 0;
for (a in arr) {
for (let k in arr[a]) {
if (k === name) {
counter++;
}
}
}
return counter;
}

How to implement a map or sorted-set in javascript

Javascript has arrays which use numeric indexes ["john", "Bob", "Joe"] and objects which can be used like associative arrays or "maps" that allow string keys for the object values {"john" : 28, "bob": 34, "joe" : 4}.
In PHP it is easy to both A) sort by values (while maintaining the key) and B) test for the existence of a value in an associative array.
$array = ["john" => 28, "bob" => 34, "joe" => 4];
asort($array); // ["joe" => 4, "john" => 28, "bob" => 34];
if(isset($array["will"])) { }
How would you acheive this functionality in Javascript?
This is a common need for things like weighted lists or sorted sets where you need to keep a single copy of a value in data structure (like a tag name) and also keep a weighted value.
This is the best I've come up with so far:
function getSortedKeys(obj) {
var keys = Object.keys(obj);
keys = keys.sort(function(a,b){return obj[a]-obj[b]});
var map = {};
for (var i = keys.length - 1; i >= 0; i--) {
map[keys[i]] = obj[keys[i]];
};
return map;
}
var list = {"john" : 28, "bob": 34, "joe" : 4};
list = getSortedKeys(list);
if(list["will"]) { }
Looking at this answer by Luke Schafer I think I might have found a better way to handle this by extending the Object.prototype:
// Sort by value while keeping index
Object.prototype.iterateSorted = function(worker, limit)
{
var keys = Object.keys(this), self = this;
keys.sort(function(a,b){return self[b] - self[a]});
if(limit) {
limit = Math.min(keys.length, limit);
}
limit = limit || keys.length;
for (var i = 0; i < limit; i++) {
worker(keys[i], this[keys[i]]);
}
};
var myObj = { e:5, c:3, a:1, b:2, d:4, z:1};
myObj.iterateSorted(function(key, value) {
console.log("key", key, "value", value)
}, 3);
http://jsfiddle.net/Xeoncross/kq3gbwgh/
With ES6 you could choose to extend the Map constructor/class with a sort method that takes an optional compare function (just like arrays have). That sort method would take two arguments, each of which are key/value pairs so that the sorting can happen on either the keys or the values (or both).
The sort method will rely on the documented behaviour of Maps that entries are iterated in insertion order. So this new method will visit the entries according to the sorted order, and then delete and immediately re-insert them.
Here is how that could look:
class SortableMap extends Map {
sort(cmp = (a, b) => a[0].localeCompare(b[0])) {
for (const [key, value] of [...this.entries()].sort(cmp)) {
this.delete(key);
this.set(key, value); // New keys are added at the end of the order
}
}
}
// Demo
const mp = new SortableMap([[3, "three"],[1, "one"],[2, "two"]]);
console.log("Before: ", JSON.stringify([...mp])); // Before
mp.sort( (a, b) => a[0] - b[0] ); // Custom compare function: sort numerical keys
console.log(" After: ", JSON.stringify([...mp])); // After
I'm not sure why none of these answers mentions the existence of a built-in JS class, Set. Seems to be an ES6 addition, perhaps that's why.
Ideally override either add or keys below... NB overriding keys doesn't even need access to the Set object's prototype. Of course you could override these methods for the entire Set class. Or make a subclass, SortedSet.
const mySet = new Set();
const mySetProto = Object.getPrototypeOf(mySet);
const addOverride = function(newObj){
const arr = Array.from(this);
arr.add(newObj);
arr.sort(); // or arr.sort(function(a, b)...)
this.clear();
for(let item of arr){
mySetProto.add.call(this, item);
}
}
mySet.add = addOverride;
const keysOverride = function(){
const arr = Array.from(this);
arr.sort(); // or arr.sort(function(a, b)...)
return arr[Symbol.iterator]();
}
mySet.keys = keysOverride;
Usage:
mySet.add(3); mySet.add(2); mySet.add(1); mySet.add(2);
for(let item of mySet.keys()){console.log(item)};
Prints out:
1 ... 2 ... 3
NB Set.keys() returns not the items in the Set, but an iterator. You could choose to return the sorted array instead, but you'd obviously be breaking the class's "contract".
Which one to override? Depends on your usage and the size of your Set. If you override both you will be duplicating the sort activity, but in most cases it probably won't matter.
NB The add function I suggest is of course naive, a "first draft": rebuilding the entire set each time you add could be pretty costly. There are clearly much cleverer ways of doing this based on examining the existing elements in the Set and using a compare function, a binary tree structure*, or some other method to determine where in it to add the candidate for adding (I say "candidate" because it would be rejected if an "identical" element, namely itself, were already found to be present).
The question also asks about similar arrangements for a sorted map... in fact it turns out that ES6 has a new Map class which lends itself to similar treatment ... and also that Set is just a specialised Map, as you might expect.
* e.g. https://github.com/Crizstian/data-structure-and-algorithms-with-ES6/tree/master/10-chapter-Binary-Tree
You usually don't sort an object. But if you do: Sorting JavaScript Object by property value
If you want to sort an array, let's say the following
var arraylist = [{"john" : 28},{ "bob": 34},{ "joe" : 4}];
You can always use Array.prototype.sort function.
Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
Maybe this code look like what you want:
Object.prototype.asort = function(){
var retVal = {};
var self = this;
var keys = Object.keys(this);
keys = keys.sort(function(a,b){return self[a] - self[b]});
for (var i = 0; i < keys.length; i++) {
retVal[keys[i]] = this[keys[i]];
}
return retVal;
}
var map = {"john" : 28, "bob": 34, "joe" : 4}
var sortedMap = map.asort();//sortedMap["will"]: undefined
If you use the open source project jinqJs its easy.
See Fiddler
var result = jinqJs()
.from([{"john" : 28},{ "bob": 34},{ "joe" : 4}])
.orderBy([{field: 0}])
.select();
Here's an implementation of OrderedMap.
Use the functions get() and set() to extract or push key value pairs to the OrderedMap.
It is internally using an array to maintain the order.
class OrderedMap {
constructor() {
this.arr = [];
return this;
}
get(key) {
for(let i=0;i<this.arr.length;i++) {
if(this.arr[i].key === key) {
return this.arr[i].value;
}
}
return undefined;
}
set(key, value) {
for(let i=0;i<this.arr.length;i++) {
if(this.arr[i].key === key) {
this.arr[i].value = value;
return;
}
}
this.arr.push({key, value})
}
values() {
return this.arr;
}
}
let m = new OrderedMap();
m.set('b', 60)
m.set('a', 10)
m.set('c', 20)
m.set('d', 89)
console.log(m.get('a'));
console.log(m.values());
https://github.com/js-sdsl/js-sdsl
The OrderedMap in Js-sdsl maybe helpful.
This is a sorted-map which implement refer to C++ STL Map.
/*
* key value
* 1 1
* 2 2
* 3 3
* Sorted by key.
*/
const mp = new OrderedMap(
[1, 2, 3].map((element, index) => [index, element])
);
mp.setElement(1, 2); // O(logn)
mp.eraseElementByKey(1) // O(logn)
// custom comparison function
mp = new OrderedMap(
[1, 2, 3].map((element, index) => [index, element]),
(x, y) => x - y
);
// enable tree iterator index (enableIndex = true)
console.log(new OrderedMap([[0, 1], [1, 1]], undefined, true).begin(),next().index); // 1

Is there a fast way to select item from array of objects in Javascript?

I have an array:
[
{
key: 'some key',
value: 'some value'
}, {
...
}, ...
]
Is there any simple way to get a certain element of this array without iterating through it and comparing each actual key with desired one?
I currently have a function
var select = function(what, from) {
for (var i in from) {
if (from[i].key == what) {
return from[i];
}
}
return null;
};
I believe there's a better way to handle it.
The short answer is no. There are lots of options to make your code cleaner - like all of the above - but you are still going to have to iterate through them one by one.
You can do 2 things to make it better:
Convert to Map
If you are going to do the search more than once or twice, then convert it to a map of the keys, so at least each subsequent lookup is O(1) instead of O(n). A number of answers suggested that - I use it a lot in my own code - but here is a basic version (there is a shorthand un underscore/lodash):
var i, hash = {};
for (i = 0; i < arr.length; i++) {
hash[arr[i].key] = arr[i].value;
}
Then for all future lookups it is a simple:
var value = hash[key];
Search Algorithms
If it needs to stay as an array for whatever reason, and you have some knowledge about what that array's values will be like, you can use all sorts of search algorithms. Cycling through them one by one is good for an even distribution, but will still be O(n), which will, on average require cycling through half the array (n/2) each time.
But search algorithms are beyond this post...
You were close. This would be the fastest form in this particular case:
var select = function(key, expected, from) {
var i;
for (i = 0; i < from.length; i += 1) {
if (from[i][key] === expected) {
return from[i];
}
}
return null;
};
select('thing', 'foo', [{thing: 'foo'}]);
// => {thing: 'foo'}
But do you require maximum speed? Or would a more elegant, general-purpose solution suit you? If so, use a predicate:
var find = function (array, predicate) {
var i;
for (i = 0; i < array.length; i += 1) {
if (predicate(array[i])) {
return array[i];
}
}
return null;
};
// Find, in an array, an item which passes the following truth test.
find([{thing: 'foo'}], function (item) {
return item.thing === 'foo';
});
// => {thing: 'foo'}
If you don't like writing boilerplate utility code like this, I recommend leveraging a library like lodash. It has a find method already and it's faster and more powerful than anything we could come up with.
Unfortunately not, however, if you alter your structure to use properties it will be much faster. Any other approach will require iteration over the array and will therefore always be less efficient than the below.
var keyValueObj = {
someKey: 'someValue',
nextKey: 'nextValue'
}
// represents 'someValue'
keyValueObj['someKey']
If you're using modern browsers, this should do the magic:
function include(arr,obj) {
return (arr.indexOf(obj) != -1);
}
or in jquery
$.inArray(value, array)
In response to your comment, check this link, this will also address your problem in the comment.
If your keys are strings use them as object properties instead of an array of objects
If your keys are objects use Map()
You can refactor your array into a map, which is, by design, good for quick and effective lookup:
{
'some key': {
value: 'some value'
},
'some other key': {
value: 'some other value'
},
}
Then your select function would be as short as
var select = function(what, from) {
return from[what];
};
If you expect return value of the select() to still have the .key property (which is redundant, as the caller already knows it as what), it is easy to achieve by either keeping the key property in map elements, or auto-adding in in select() before returning.
If you need to run this function very often, I would recommend creating an index:
var yourItemList = [{...}],
select = (function (items) {
var i = 0,
l = items.length,
preparedItems = {};
for (; i < l; ++i) {
preparedItems[items[i].key] = items[i].value;
}
return function (key) {
return preparedItems[key];
};
}(yourItemList));
This way you only iterate once on the item list. After that every select('theKeyYouNeed') just uses the mapped "index".
You can filter array. It is build in Array method so you don't need to declare other helper functions
var arr = [
{
key: 'some key',
value: 'some value'
}, {
key: 'some key1',
value: 'some value'
}
];
arr = arr.filter(function(item) {
return item.key === 'some key'
});
Now arr has only first object (because its key equal to 'some key'). Object that you find is arr[0].
You can use find() instead of filter() when you need just one object. Note, that find() is part of ES6 and has no support in all major browsers

How to find common properties between JavaScript objects

What is the best/most efficient way to find the common/different properties of an array of objects.
I need to identify the set of properties that exists in all objects and all have the same value(the common).
Preferably I would also like to get an array with all other properties (the diff).
I have searched for an efficient library/function that can do it. But didn't find anything. So I tried on my own.
Consider this array of JS objects:
var objects = [{
id: '2j2w4f',
color: 'red',
height: 20,
width: 40,
owner: 'bob'
}, {
id: '2j2w3f',
color: 'red',
height: 20,
width: 41,
owner: 'bob'
}, {
id: '2j2w2f',
color: 'red',
height: 21,
}, {
id: '2j2w1f',
color: 'red',
height: 21,
width: 44
}];
I would like to identify color (with value red) as the only common property.
Note that they do not have the same set of properties. E.g. owner is not a common property.
This is my own attempt to solve it (using lodash):
function commonDifferentProperties(objects) {
// initialize common as first object, and then remove non-common properties.
var common = objects[0];
var different = [];
var i, obj;
// iterate through the rest (note: i === 0 is not supposed to be covered by this)
for (i = objects.length - 1; i > 0; i--) {
obj = objects[i];
// compare each property of obj with current common
_.forOwn(obj, function (value, key) {
// if property not in current common it must be different
if (_.isUndefined(common[key])) {
if (!_.contains(different, key)) {
different.push(key);
}
} else if (common[key] !== value) { // remove property from common if value is not the same
delete common[key];
different.push(key);
}
});
// check that all properties of common is present in obj, if not, remove from common.
_.forOwn(common, function (value, key) {
if (_.isUndefined(obj[key])) {
delete common[key];
different.push(key);
}
});
}
return {
common: common,
different: different
};
}
jsFiddle with the example
I have also tried a mapReduce approach, but that seemed even worse.
I still think this seems a bit complex/time consuming, and I will do this on 1000-10000 objects or more with 20-50 properties each.
Any suggestions?
There are two things that are look wrong in your solution:
By var common = objects[0]; you don't copy the object, so you're going to corrupt the objects
You both check that all properties of common is present in obj, but also compare each property of obj with current common. That seems to be once too much. Didn't realize at first that you needed the different properties as well.
I'd loop over the data in two passes. In the first, you collect all apparent properties in one object, in the second you test whether they're common or not:
function commonDifferentProperties(objects) {
var common = _.reduce(objects, function(acc, obj) {
for (var p in obj)
acc[p] = obj[p];
return acc;
}, {});
var different = _.reduce(objects, function(acc, obj) {
for (var p in common)
if (common[p] !== obj[p]) {
delete common[p];
acc.push(p);
}
return acc;
}, []);
return {
common: common,
different: different
};
}
Here's what I did using just vanilla JS:
function commonDifferentProperties(objects) {
var common = JSON.parse(JSON.stringify(objects[0]));
var unmatchedProps = {};
for (var i = 1; i < objects.length; i++) {
for (var prop in objects[i]) {
checkProps(objects[i],common,prop);
}
for (var commProp in common) {
checkProps(common,objects[i],commProp);
}
}
console.log(common); // this is all the matched key/value pairs
console.log(unmatchedProps); // this is all the unmatched keys
return { common: common, different: unmatchedProps };
function checkProps(source, target, prop) {
if (source.hasOwnProperty(prop)) {
var val = source[prop];
if (!target.hasOwnProperty(prop) || target[prop] !== val) {
unmatchedProps[prop] = true; // note: you could extend this to store values, or number of times you found this key, or whatever
delete common[prop];
}
}
}
}
http://jsfiddle.net/TwbPA/
So I copy the first object and use that to keep track of keys and values that are common. Then I iterate through all the other objects in your array and first look through all the key/values in the common object and compare to the current, deleting any missing properties from the common object if they are not in the current one, then I do the reverse to catch any properties in the current object that aren't in the common (or are in the current, but have the wrong value).
Edit
Sorry, i was in a hurry and didn't had enough time to think about it.
Indeed, there's no need for a sort. I was thinking using a binary algorithm or something..
Here, the updated code without the sort. Console.time() gave me '3ms'.
I'm doing similar to Bergi's solution, but instead of collecting all apparant properties i search for the element that has the fewest amount of properties. This reduces the amount of iterations on the second loop.
I've based the code on the following:
if object X has a property the selected object doesn't have, then it isn't a common property!
Thus the selected object has all common properties + extra's.
The selected object has the fewest properties, thus the iterations of validating is less.
http://jsfiddle.net/kychan/cF3ne/1/
// returns the common properties of given array.
function getCommonProps(objects)
{
// storage var for object with lowest properties.
var lowest = {obj:null, nProperties:1000};
// search for the object with lowest properties. O(n).
for (var j in objects)
{
var _nProp = Object.keys(objects[j]).length;
if (_nProp < lowest.nProperties)
lowest = {obj:objects[j], nProperties:_nProp};
}
// var that holds the common properties.
var retArr = [];
// The object with the fewest properties should contain common properties.
for (var i in lowest.obj)
if (isCommonProp(objects, i)) retArr.push(i);
return retArr;
}
// Checks if the prop exists in all objects of given array.
function isCommonProp(arr, prop)
{
for (var i in arr)
{
if (arr[i][prop]===undefined)
return false;
}
return true;
}
console.time('getCommonProps()_perf');
console.log(getCommonProps(objects));
console.timeEnd('getCommonProps()_perf');
Here's another approach that uses reduce() and transform():
_.reduce(objects, function(result, item) {
if (_.isEmpty(result)) {
return _.assign({}, item);
}
return _.transform(item, function(common, value, key) {
if (result[key] === value) {
common[key] = value;
}
}, {});
}, {});

Array within object returning length of 0, even though there are elements present

I am trying to implement a Trie in Javascript, which is easy enough but I seem to have hit a road block with my object.
The nodes are structured as follows:
var node = {
children: []
}
Children is an array of nodes that is mapped by a letter in a string. So the string "Test" would look like this:
root = {
children: [
't' => {
children: [
'e' => {
children: [
's' => {
children: [
't' => {
children: []
}
]
}
]
}
]
}
]
};
So each children array should have a length of 1, but if do something like alert(this._root.children.length); I get zero. Any thoughts on why this is happening?
Here is the rest of my implementation:
function Trie() {
this._root = {
children: []
};
}
Trie.prototype = {
//restore constructor
constructor: Trie,
add: function (str){
var curr = this._root,
prev,
currchar;
// For each character in the string
for(var i = 0, j = str.length; i < j; i++) {
// Insert only lowercase letters for efficiency
currchar = str.toLowerCase().charAt(i);
prev = curr;
curr = prev.children[currchar];
// Traverse until we hit a non-existant node
if(typeof(curr) == "undefined") {
// Make a new node
prev.children[currchar] = {
children: []
};
curr = prev.children[currchar];
}
}
}
You are adding properties to the array instance object, not elements to the array. The length property only includes array elements, not properties on the array instance object.
var a = [23, 42];
console.log(a.length); // 2
a['foo'] = 'bar';
console.log(a.length); // 2
a[2] = 1337;
console.log(a.length); // 3
EDITED:
You could instead structure the nodes like this:
var node = {
children: {},
length: function () {
var i = 0;
var k;
for (k in this.children) {
if (this.children.hasOwnProperty(k)) {
i++;
}
}
return i;
}
};
This is inefficient, of course. You should instead define a Node class with the length method on its prototype. Alternatively, define an add method that updates the length property.
I think that the problem is that you use a javasrcipt array as an associative array (as found in other languages). In javascript "associative" arrays are objects that don't have a length property. Normal arrays have numeric indices.
Irrelevant to the question but you might find this useful.
Maybe you want
str.toLowerCase().charCodeAt(i)
instead of
str.toLowerCase().charAt(i)
If str is "f1", the properties you're adding to the children array are "f" and "1" which should cause an array with property named f and length 0, and another child array with length 2 and property 1.
To get only numeric properties, you should make sure your property names are valid array indices -- positive integers representable in 31 bits.
By using charCodeAt instead of charCode, you would get the property names 102 and 49 instead of "f" and 1.

Categories