Can anyone recommend a good Hashtable implementation in Javascript? [duplicate] - javascript

I need to store some statistics using JavaScript in a way like I'd do it in C#:
Dictionary<string, int> statistics;
statistics["Foo"] = 10;
statistics["Goo"] = statistics["Goo"] + 1;
statistics.Add("Zoo", 1);
Is there an Hashtable or something like Dictionary<TKey, TValue> in JavaScript?
How could I store values in such a way?

Use JavaScript objects as associative arrays.
Associative Array: In simple words associative arrays use Strings instead of Integer numbers as index.
Create an object with
var dictionary = {};
JavaScript allows you to add properties to objects by using the following syntax:
Object.yourProperty = value;
An alternate syntax for the same is:
Object["yourProperty"] = value;
If you can, also create key-to-value object maps with the following syntax:
var point = { x:3, y:2 };
point["x"] // returns 3
point.y // returns 2
You can iterate through an associative array using the for..in loop construct as follows
for(var key in Object.keys(dict)){
var value = dict[key];
/* use key/value for intended purpose */
}

var associativeArray = {};
associativeArray["one"] = "First";
associativeArray["two"] = "Second";
associativeArray["three"] = "Third";
If you are coming from an object-oriented language you should check this article.

All modern browsers support a JavaScript Map object. There are a couple of reasons that make using a Map better than Object:
An Object has a prototype, so there are default keys in the map.
The keys of an Object are Strings, where they can be any value for a Map.
You can get the size of a Map easily while you have to keep track of size for an Object.
Example:
var myMap = new Map();
var keyObj = {},
keyFunc = function () {},
keyString = "a string";
myMap.set(keyString, "value associated with 'a string'");
myMap.set(keyObj, "value associated with keyObj");
myMap.set(keyFunc, "value associated with keyFunc");
myMap.size; // 3
myMap.get(keyString); // "value associated with 'a string'"
myMap.get(keyObj); // "value associated with keyObj"
myMap.get(keyFunc); // "value associated with keyFunc"
If you want keys that are not referenced from other objects to be garbage collected, consider using a WeakMap instead of a Map.

Unless you have a specific reason not to, just use a normal object. Object properties in JavaScript can be referenced using hashtable-style syntax:
var hashtable = {};
hashtable.foo = "bar";
hashtable['bar'] = "foo";
Both foo and bar elements can now then be referenced as:
hashtable['foo'];
hashtable['bar'];
// Or
hashtable.foo;
hashtable.bar;
Of course this does mean your keys have to be strings. If they're not strings they are converted internally to strings, so it may still work. Your mileage may vary.

Since every object in JavaScript behaves like - and is generally implemented as - a hashtable, I just go with that...
var hashSweetHashTable = {};

In C# the code looks like:
Dictionary<string,int> dictionary = new Dictionary<string,int>();
dictionary.add("sample1", 1);
dictionary.add("sample2", 2);
or
var dictionary = new Dictionary<string, int> {
{"sample1", 1},
{"sample2", 2}
};
In JavaScript:
var dictionary = {
"sample1": 1,
"sample2": 2
}
A C# dictionary object contains useful methods, like dictionary.ContainsKey()
In JavaScript, we could use the hasOwnProperty like:
if (dictionary.hasOwnProperty("sample1"))
console.log("sample1 key found and its value is"+ dictionary["sample1"]);

If you require your keys to be any object rather than just strings, then you could use my jshashtable.

Note:
Several years ago, I had implemented the following hashtable, which has had some features that were missing to the Map class. However, that's no longer the case — now, it's possible to iterate over the entries of a Map, get an array of its keys or values or both (these operations are implemented copying to a newly allocated array, though — that's a waste of memory and its time complexity will always be as slow as O(n)), remove specific items given their key, and clear the whole map.
Therefore, my hashtable implementation is only useful for compatibility purposes, in which case it'd be a saner approach to write a proper polyfill based on this.
function Hashtable() {
this._map = new Map();
this._indexes = new Map();
this._keys = [];
this._values = [];
this.put = function(key, value) {
var newKey = !this.containsKey(key);
this._map.set(key, value);
if (newKey) {
this._indexes.set(key, this.length);
this._keys.push(key);
this._values.push(value);
}
};
this.remove = function(key) {
if (!this.containsKey(key))
return;
this._map.delete(key);
var index = this._indexes.get(key);
this._indexes.delete(key);
this._keys.splice(index, 1);
this._values.splice(index, 1);
};
this.indexOfKey = function(key) {
return this._indexes.get(key);
};
this.indexOfValue = function(value) {
return this._values.indexOf(value) != -1;
};
this.get = function(key) {
return this._map.get(key);
};
this.entryAt = function(index) {
var item = {};
Object.defineProperty(item, "key", {
value: this.keys[index],
writable: false
});
Object.defineProperty(item, "value", {
value: this.values[index],
writable: false
});
return item;
};
this.clear = function() {
var length = this.length;
for (var i = 0; i < length; i++) {
var key = this.keys[i];
this._map.delete(key);
this._indexes.delete(key);
}
this._keys.splice(0, length);
};
this.containsKey = function(key) {
return this._map.has(key);
};
this.containsValue = function(value) {
return this._values.indexOf(value) != -1;
};
this.forEach = function(iterator) {
for (var i = 0; i < this.length; i++)
iterator(this.keys[i], this.values[i], i);
};
Object.defineProperty(this, "length", {
get: function() {
return this._keys.length;
}
});
Object.defineProperty(this, "keys", {
get: function() {
return this._keys;
}
});
Object.defineProperty(this, "values", {
get: function() {
return this._values;
}
});
Object.defineProperty(this, "entries", {
get: function() {
var entries = new Array(this.length);
for (var i = 0; i < entries.length; i++)
entries[i] = this.entryAt(i);
return entries;
}
});
}
Documentation of the class Hashtable
Methods:
get(key)
Returns the value associated to the specified key.
Parameters:
key: The key from which to retrieve the value.
put(key, value)
Associates the specified value to the specified key.
Parameters:
key: The key to which associate the value.
value: The value to associate to the key.
remove(key)
Removes the specified key, together with the value associated to it.
Parameters:
key: The key to remove.
clear()
Clears the whole hashtable, by removing all its entries.
indexOfKey(key)
Returns the index of the specified key, according to the order entries have been added.
Parameters:
key: The key of which to get the index.
indexOfValue(value)
Returns the index of the specified value, according to the order entries have been added.
Parameters:
value: The value of which to get the index.
Remarks:
Values are compared by identity.
entryAt(index)
Returns an object with a key and a value properties, representing the entry at the specified index.
Parameters:
index: The index of the entry to get.
containsKey(key)
Returns whether the hashtable contains the specified key.
Parameters:
key: The key to look for.
containsValue(value)
Returns whether the hashtable contains the specified value.
Parameters:
value: The value to look for.
forEach(iterator)
Iterates through all the entries in the hashtable, calling specified iterator.
Parameters:
iterator: A method with three parameters, key, value and index, where index represents the index of the entry according to the order it's been added.
Properties:
length (Read-only)
Gets the count of the entries in the hashtable.
keys (Read-only)
Gets an array of all the keys in the hashtable.
values (Read-only)
Gets an array of all the values in the hashtable.
entries (Read-only)
Gets an array of all the entries in the hashtable. They're represented the same as the method entryAt().

function HashTable() {
this.length = 0;
this.items = new Array();
for (var i = 0; i < arguments.length; i += 2) {
if (typeof (arguments[i + 1]) != 'undefined') {
this.items[arguments[i]] = arguments[i + 1];
this.length++;
}
}
this.removeItem = function (in_key) {
var tmp_previous;
if (typeof (this.items[in_key]) != 'undefined') {
this.length--;
var tmp_previous = this.items[in_key];
delete this.items[in_key];
}
return tmp_previous;
}
this.getItem = function (in_key) {
return this.items[in_key];
}
this.setItem = function (in_key, in_value) {
var tmp_previous;
if (typeof (in_value) != 'undefined') {
if (typeof (this.items[in_key]) == 'undefined') {
this.length++;
} else {
tmp_previous = this.items[in_key];
}
this.items[in_key] = in_value;
}
return tmp_previous;
}
this.hasItem = function (in_key) {
return typeof (this.items[in_key]) != 'undefined';
}
this.clear = function () {
for (var i in this.items) {
delete this.items[i];
}
this.length = 0;
}
}

https://gist.github.com/alexhawkins/f6329420f40e5cafa0a4
var HashTable = function() {
this._storage = [];
this._count = 0;
this._limit = 8;
}
HashTable.prototype.insert = function(key, value) {
// Create an index for our storage location by passing
// it through our hashing function
var index = this.hashFunc(key, this._limit);
// Retrieve the bucket at this particular index in
// our storage, if one exists
//[[ [k,v], [k,v], [k,v] ] , [ [k,v], [k,v] ] [ [k,v] ] ]
var bucket = this._storage[index]
// Does a bucket exist or do we get undefined
// when trying to retrieve said index?
if (!bucket) {
// Create the bucket
var bucket = [];
// Insert the bucket into our hashTable
this._storage[index] = bucket;
}
var override = false;
// Now iterate through our bucket to see if there are any conflicting
// key value pairs within our bucket. If there are any, override them.
for (var i = 0; i < bucket.length; i++) {
var tuple = bucket[i];
if (tuple[0] === key) {
// Override value stored at this key
tuple[1] = value;
override = true;
}
}
if (!override) {
// Create a new tuple in our bucket.
// Note that this could either be the new empty bucket we created above
// or a bucket with other tupules with keys that are different than
// the key of the tuple we are inserting. These tupules are in the same
// bucket because their keys all equate to the same numeric index when
// passing through our hash function.
bucket.push([key, value]);
this._count++
// Now that we've added our new key/val pair to our storage
// let's check to see if we need to resize our storage
if (this._count > this._limit * 0.75) {
this.resize(this._limit * 2);
}
}
return this;
};
HashTable.prototype.remove = function(key) {
var index = this.hashFunc(key, this._limit);
var bucket = this._storage[index];
if (!bucket) {
return null;
}
// Iterate over the bucket
for (var i = 0; i < bucket.length; i++) {
var tuple = bucket[i];
// Check to see if key is inside bucket
if (tuple[0] === key) {
// If it is, get rid of this tuple
bucket.splice(i, 1);
this._count--;
if (this._count < this._limit * 0.25) {
this._resize(this._limit / 2);
}
return tuple[1];
}
}
};
HashTable.prototype.retrieve = function(key) {
var index = this.hashFunc(key, this._limit);
var bucket = this._storage[index];
if (!bucket) {
return null;
}
for (var i = 0; i < bucket.length; i++) {
var tuple = bucket[i];
if (tuple[0] === key) {
return tuple[1];
}
}
return null;
};
HashTable.prototype.hashFunc = function(str, max) {
var hash = 0;
for (var i = 0; i < str.length; i++) {
var letter = str[i];
hash = (hash << 5) + letter.charCodeAt(0);
hash = (hash & hash) % max;
}
return hash;
};
HashTable.prototype.resize = function(newLimit) {
var oldStorage = this._storage;
this._limit = newLimit;
this._count = 0;
this._storage = [];
oldStorage.forEach(function(bucket) {
if (!bucket) {
return;
}
for (var i = 0; i < bucket.length; i++) {
var tuple = bucket[i];
this.insert(tuple[0], tuple[1]);
}
}.bind(this));
};
HashTable.prototype.retrieveAll = function() {
console.log(this._storage);
//console.log(this._limit);
};
/******************************TESTS*******************************/
var hashT = new HashTable();
hashT.insert('Alex Hawkins', '510-599-1930');
//hashT.retrieve();
//[ , , , [ [ 'Alex Hawkins', '510-599-1930' ] ] ]
hashT.insert('Boo Radley', '520-589-1970');
//hashT.retrieve();
//[ , [ [ 'Boo Radley', '520-589-1970' ] ], , [ [ 'Alex Hawkins', '510-599-1930' ] ] ]
hashT.insert('Vance Carter', '120-589-1970').insert('Rick Mires', '520-589-1970').insert('Tom Bradey', '520-589-1970').insert('Biff Tanin', '520-589-1970');
//hashT.retrieveAll();
/*
[ ,
[ [ 'Boo Radley', '520-589-1970' ],
[ 'Tom Bradey', '520-589-1970' ] ],
,
[ [ 'Alex Hawkins', '510-599-1930' ],
[ 'Rick Mires', '520-589-1970' ] ],
,
,
[ [ 'Biff Tanin', '520-589-1970' ] ] ]
*/
// Override example (Phone Number Change)
//
hashT.insert('Rick Mires', '650-589-1970').insert('Tom Bradey', '818-589-1970').insert('Biff Tanin', '987-589-1970');
//hashT.retrieveAll();
/*
[ ,
[ [ 'Boo Radley', '520-589-1970' ],
[ 'Tom Bradey', '818-589-1970' ] ],
,
[ [ 'Alex Hawkins', '510-599-1930' ],
[ 'Rick Mires', '650-589-1970' ] ],
,
,
[ [ 'Biff Tanin', '987-589-1970' ] ] ]
*/
hashT.remove('Rick Mires');
hashT.remove('Tom Bradey');
//hashT.retrieveAll();
/*
[ ,
[ [ 'Boo Radley', '520-589-1970' ] ],
,
[ [ 'Alex Hawkins', '510-599-1930' ] ],
,
,
[ [ 'Biff Tanin', '987-589-1970' ] ] ]
*/
hashT.insert('Dick Mires', '650-589-1970').insert('Lam James', '818-589-1970').insert('Ricky Ticky Tavi', '987-589-1970');
hashT.retrieveAll();
/* NOTICE HOW THE HASH TABLE HAS NOW DOUBLED IN SIZE UPON REACHING 75% CAPACITY, i.e. 6/8. It is now size 16.
[,
,
[ [ 'Vance Carter', '120-589-1970' ] ],
[ [ 'Alex Hawkins', '510-599-1930' ],
[ 'Dick Mires', '650-589-1970' ],
[ 'Lam James', '818-589-1970' ] ],
,
,
,
,
,
[ [ 'Boo Radley', '520-589-1970' ],
[ 'Ricky Ticky Tavi', '987-589-1970' ] ],
,
,
,
,
[ [ 'Biff Tanin', '987-589-1970' ] ] ]
*/
console.log(hashT.retrieve('Lam James')); // 818-589-1970
console.log(hashT.retrieve('Dick Mires')); // 650-589-1970
console.log(hashT.retrieve('Ricky Ticky Tavi')); //987-589-1970
console.log(hashT.retrieve('Alex Hawkins')); // 510-599-1930
console.log(hashT.retrieve('Lebron James')); // null

You can create one using like the following:
var dictionary = { Name:"Some Programmer", Age:24, Job:"Writing Programs" };
// Iterate over using keys
for (var key in dictionary) {
console.log("Key: " + key + " , " + "Value: "+ dictionary[key]);
}
// Access a key using object notation:
console.log("Her name is: " + dictionary.Name)

Related

Pushing to array

I need to loop through array and each array in array that has extra values, push them to their parent array as separate item. I hope this makes sense..
This is the structure of my initial array:
{type:
[ 0:
value: "tomato"
],
[ 1:
{value: "apple",
[ extras:
[ 0: { value: "green" } ],
[ 1: { value: "red" } ]
]
],
[ 2:
value: "pineapple"
]
}
What the result would have to look like:
[type:
[ 0:
tomato
],
[ 1:
apple,
green,
red
],
[ 2:
pineapple
]
]
What I've tried and failed: (I also commented the error I get on right line)
var response = /* json of first codeblock in question is response from ajax */;
var items = JSON.parse( response );
var type = Object.keys( items )[0];
var myArray = []
var count = items[type].lenght;
//Loop through main items in "type"
for( i = 0; i < count; i++ ) {
var value = items[type][i][value];
myArray[type][i] = [value]; //Uncaught TypeError: Cannot set property '0' of undefined
if( items[type][i][extras] ) {
var extracount = items[type][i][extras].lenght;
//Loop through extras
for( k = 0; k < extracount; k++ ) {
var extra = items[type][i][extras][k][value];
myArray[type][i].push( extra );
}
}
}
My main problem that I don't understand and that seems to be the problem in my example as well:
If I declare an empty array, how do I:
push an item to that array also declaring a new array around that item?
push another item to that array that was made around the first item?
This is what I believe you want. The following code may be incorrect, because I'm approximating what I believe your items object contains.
var items = {
type: [
{
value: "tomato"
},
{
value: "apple",
extras: [
{
value: "green"
}, {
value: "red"
}
]
},
{
value: "pineapple"
}
]
};
var myArray = {
type: []
};
var count = items['type'].length;
//Loop through main items in "type"
for (i = 0; i < count; i++) {
var subarray = [];
subarray.push(items['type'][i]['value']);
if (items['type'][i]['extras']) {
var extracount = items['type'][i]['extras'].length;
//Loop through extras
for (k = 0; k < extracount; k++) {
var extra = items['type'][i]['extras'][k]['value'];
subarray.push(extra);
}
}
myArray['type'].push(subarray);
}
Some notes:
You will definitely need to learn the difference between an array and an object in javascript. There are plenty of resources online for this.
When retrieving/manipulating a property prop from an object obj (i.e. for a key-value pair), you will need to use obj.prop or obj['prop']. Note the use of a string in the latter example.
For an array arr, you should use arr.push(value) to push a new value onto the array.
Your problem is here:
var value = items[type][i][value];
you should change it to
var value = items[type][i].value;

Check for duplicates in an array

I have a function that will check a serialized form data if there are duplicates values in it.
s = $('#multiselectForm').serialize();
var x = [];
var y = [];
x = s.split("&");
for (var i = x.length - 1; i >= 0; i--) {
y.push(x[i].split("="));
};
var c = 0;
var e = 0;
for (var i = y.length - 1; i >= 0; i--) {
if (y[i][1] == y[c][1]) {
e++;
$('.duplicateAlert').show();
} else {
$('.duplicateAlert').hide();
};
c++;
};
Basically, what it does is split the string produced by the serialize() function and push the data into arrays.
The array I'm trying to parse looks like this:
Array [
Array [
0: 'my_field1',
1: 'val1'
],
Array [
0: 'my_field2'
1: 'val2'
],
Array [
0: 'my_field3'
1: 'val1'
]
]
Are there any better ways to do the same task? Maybe even shorter?
Create an empty array to hold the matches
Loop through the array. On each iteration...
Loop through the matches array and check if an item with the same value exists. If it does, set the matched flag.
Check if the matched flag has been set
if so, alert the user
if not add the item to matches.
var array = [
[ 'my_field1', 'val1' ],
[ 'my_field2', 'val2' ],
[ 'my_field3', 'val1' ],
[ 'my_field4', 'val2' ],
[ 'my_field5', 'val3' ]
], matches = [], match = false;
for(var i = 0, j = array.length; i < j; i++) {
match = false;
for(var k = 0, l = matches.length; k < l; k++) {
if(matches[k][1] == array[i][1]) {
match = true;
}
}
if(match) alert('Duplicate!');
else matches.push(array[i]);
}
If you have serialised data in the typical format like:
var data = 'foo=foo&bar=bar%26bar&blah=foo';
then you can check it for duplicates by getting the values between = and & and looking for dupes:
var seen = {};
var hasDupes = (data.match(/=[^&]+/g) || []).some(function(v){
return v in seen || (seen[v] = true) && false;
});
console.log(hasDupes); // true
The idea behind:
data.match(/=[^&]+/g) || []
is that match can return null if no matches are found, so if that happens the expression returns an empty array and the following call to some is called on the empty array (and returns false) rather than null, and hence doesn't throw the error that it would otherwise.
However, I still think it would be more efficient to check the form control values directly before serialising, rather than serialising the form then checking the result.
You can do that with a function like:
function checkDupValues(form) {
var value,
seen = {},
controls = form.elements;
for (var i=0, iLen=controls.length; i<iLen; i++) {
// Might want to check type of control here and ignore buttons, etc.
value = controls[i].value;
// Ignore empty controls?
if (value != '' && value in seen) {
// have a duplicate value that is not ''
alert('have dupes');
} else {
seen[value] = true;
}
}
}
Try this although its not much shorter:
var array = [
[
'my_field1',
'val1'
],
[
'my_field2',
'val2'
],
[
'my_field3',
'val1'
]
]
var originals = [];
var duplicates = [];
for (a in array) {
if (originals.indexOf(array[a][1] == -1)) {
originals.push(array[a][1])
} else {
duplicates.push(array[a])
}
}
alert('duplicates: ' + duplicates.join(', '));

Trying to loop through arrays containing arrays containing objects, to match data

I'm tryign to write code that will loop through an array "productsArray" and match it against my productPropertyArray to pull matching information.
however productsArray is an array in an array that contains an object with the data. My Question is how can I loop through both arrays and then return the matching data.
Current function:
var pList = productsArray
if (productPropertyArray.length === 0 || productsArray.length === 0) return [];
for (var i = 0; i < pList.length; i++) {
for (var j = 0; j < pList[i].length; j++) {
if (pList[i][j] === productPropertyArray) {
return productPropertyArray;
} else {
continue;
}
}
}
return [];
};
example of pList:
productsArray = [
[{"sku" : "131674"},
{"sku" : "84172"}],
[{"productID" : "1234"}
,{"productID" : "12345"}],
[{"test": 1},{"test": 1}],
[{"test": 1},{"sellAlone": false,"test": 1}],
[{"test": 1}],
[{"sellAlone": false,"test": 1}]
];
example of productPropertyArray: (its an argument thats replaced by the following)
productSKUArray = [
"00544MF24F575",
"131674",
"84172"
];
productPropertyArray is just an argument in the function which is replaced by productSKUArray The setup goes like this: function(productProperty, productPropertyArray, productsArray) {
productProperty is just a string that contains sku or productID
any ideas are appreciated. thanks.
Check this out:
http://jsfiddle.net/v9d7bjms/2/
function find() {
var productsArray = [
[{"sku" : "131674"},
{"sku" : "84172"}],
[{"productID" : "1234"}
,{"productID" : "12345"}],
[{"test": 1},{"test": 1}],
[{"test": 1},{"sellAlone": false,"test": 1}],
[{"test": "00544MF24F575"}],
[{"sellAlone": false,"test": 1}]
],
pList = productsArray,
productSKUArray = [
"00544MF24F575",
"131674",
"84172"
];
// All arrays matching your productsSKUArray
var findings = productsArray.filter(function (productProperty) {
// .some returns true after finding first matching element (and breaks the loop)
return productProperty.some(function (obj) {
var keys = Object.keys(obj);
// We need to get all the "values" from object so we interate over
// the keys and check if any value matches something from productSKUArray
return keys.some(function (key) {
// Check if value exists in productsSKUArray
return productSKUArray.indexOf(obj[key]) > -1;
});
});
});
return findings;
}
console.log(find());
.filter will return all arrays containing objects with values from productSKUArray.
See Array.prototype.filter, Array.prototype.some and Array.prototype.indexOf for method reference.
The inner if needs to refer to pList[i][j].
This will output [{sku: "131674"}, {sku: "84172"}].
var matchingData = [];
for(var productProperties in productsArray){
var pp = productsArray[productProperties];
for(var property in pp) {
var p = pp[property];
for(var propertyName in p){
var propertyValue = p[propertyName];
for(var i in productSKUArray){
if(propertyValue == productSKUArray[i]){
matchingData.push(p);
break;
}
}
}
}
}
but this is just the brute force solution.

Check if an object with index is in array

$.each(constructions, function(i,v) {
if ($.inArray(v.name, map[ii].buildings) == -1) {//stuff}
};
Where constructions is an array of objects, each with a unique name. map[ii].buildings is an array containing some of these objects. I want to iterate each object in constructions, checking if its name parameter appears in the objects of map[ii].buildings.
The above code works if the each element in the map[ii].buildings array is just the text string of the object name, but not if the element is the entire object.. close, but no dice >.<
Try using $.grep() instead of $.inArray(); you can specify a function to do the filtering for you.
Instead of checking for -1, you check whether the array that $.grep() returns has length == 0
Simple example: (would be easier if you posted the code / example of what "constructions" objects look like)
var constructions = [{
Name: "Mess hall",
SqFt: 5000
}, {
Name: "Infirmary",
SqFt: 2000
}, {
Name: "Bungalow",
SqFt: 2000
}, {
Name: "HQ",
SqFt: 2000
}];
var buildings = [{
Name: "Infirmary",
SqFt: 2000
}, {
Name: "HQ",
SqFt: 2000
}];
// found buildings will be list of items in "constructions" that is not in "buildings"
var foundBuildings = $.grep(constructions, function (constructionsItem) {
return $.grep(buildings, function (buildingsItem) {
return buildingsItem.Name === constructionsItem.Name
}).length == 0; // == 0 means "not in", and > 0 means "in"
});
// this just renders the results all pretty for ya
$.each(foundBuildings, function (idx, item) {
$("#output").append("<div>" + item.Name + "</div>");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id='output'></div>
Example jsFiddle: http://jsfiddle.net/eLeuy9eg/3/
The non-jQuery way of doing this would be to use filter. Something like this:
// pass in an array and the key for which you want values
// it returns an array of those values
function getValues(arr, key) {
return arr.map(function (el) { return el[key]; });
}
function notFoundIn(arr, arr2) {
// grab the names of the buildings
var buildings = getValues(arr2, 'name');
// grab the names from the construction objects and filter
// those that are not in the building array
return getValues(arr, 'name').filter(function (el) {
return buildings.indexOf(el) === -1;
});
}
notFoundIn(constructions, buildings); // eg [ "one", "three" ]
DEMO
You could even add a new method to the array prototype. With this one you can use either simple arrays, or arrays of objects if you pass in a key. Note in this example I've replaced map and filter with loops that perform the same functions, but faster (see comments):
function getValues(arr, key) {
var out = [];
for (var i = 0, l = arr.length; i < l; i++) {
out.push(arr[i][key]);
}
return out;
}
if (!Array.prototype.notFoundIn) {
Array.prototype.notFoundIn = function (inThisArray, key) {
var thisArr = key ? getValues(this, key) : this;
var arrIn = key ? getValues(inThisArray, key) : inThisArray;
var out = [];
for (var i = 0, l = thisArr.length; i < l; i++) {
if (arrIn.indexOf(thisArr[i]) === -1) {
out.push(thisArr[i]);
}
}
return out;
}
}
constructions.notFoundIn(buildings, 'name');
[1, 2, 3].notFoundIn([2]); // [1, 3]
DEMO

Combining results into one object

I'm looping through a set of inputs. I need to tally up the grouped totals. The inputs below to one of three categories.
How do I go about combining the values up relevant to three categories?
var compoundedArray = new Array();
holder.find(".dataset input").each(function(index) {
var val = $(this).val();
var dataType = $(this).data("type");
var localObj = {};
localObj[dataType] = val;
compoundedArray.push(localObj);
});
I have an object like this
[
{
"growth":30
},
{
"growth": 40
},
{
"other": 20
}
]
how do I loop through the object to produce something like
[
{
"growth": 70
},
{
"other": 20
}
]
if I looped over the initial array object
for (var i = 0; i < compoundedArray.length; i++) {
console.log(compoundedArray[i]);
}
how would I go about checking to ensure I don't have duplicates - and that I can tally up the results?
Ideally the resulting format may be the best
var array = [
"matching": 50,
"growth": 20
]
var array = [
"matching": 50,
"growth": 20
]
is not valid JS, but you can create an object of the form
var obj = {
"matching": 50,
"growth": 20
};
And that's pretty easy to do, just use an object from the very beginning:
var result = {};
holder.find(".dataset input").each(function(index) {
var val = +$(this).val(); // use unary plus to convert to number
var dataType = $(this).data("type");
result[dataType] = (result[dataType] || 0) + val;
});
Further reading material:
MDN - Working with Objects
Eloquent JavaScript - Data structures: Objects and Arrays
You can just use an object (not array) with unique keys.
var compoundedObj = {};
$(".dataset input", holder).each(function() {
var dataType = $(this).data("type");
if(!compoundedObj.hasOwnProperty(dataType)) {
compoundedObj[dataType] = 0;
}
compoundedObj[dataType] += parseInt($(this).val(), 10);
});
In this way you'll get an object like this:
{
"growth": 70,
"other": 20
}
Live demo
http://jsfiddle.net/GFwGU/
var original = [{"growth":30},{"growth": 40},{"other": 20}]
// object to sum all parts by key
var sums = {}
// loop through original object
for(var index in original){
// get reference to array value (target object)
var outer = original[index]
// loop through keys of target object
for(var key in outer){
// get a reference to the value
var value = outer[key]
// set or add to the value on the sums object
sums[key] = sums[key] ? sums[key] + value : value
}
}
// create the output array
var updated = []
// loop through all the summed keys
for(var key in sums){
// get reference to value
var value = sums[key]
// create empty object
var dummy = {}
// build object into desired format
dummy[key] = value
// push to output array
updated.push(dummy)
}
// check the results
alert(JSON.stringify( updated ))
var add=function (a,b){ a=a||0; b=b||0; return a+b};
var input=[ {growth:30},{growth:40},{other:20} ],output=[],temp={};
$.each(input,function(i,o){
var n;
for(i in o)
{n=i;break}
temp[n]=add(temp[n],o[n]);
});
$.each(temp,function(i,o){
var k={};
k[i]=o;
output.push(k)
});

Categories