Hashmaps in javascript from integer keys - javascript

I am coding in javascript & I need HashMap type structure .
Normally when I need hashmaps , I would use associative arrays only (with strings as keys).
But this time I need integers as keys to hashmaps.
So if I try to store A[1000]=obj, 1001 sized array is created & A[1001] is put as obj.
Even if I try A["1000"]=obj , it still allocates 1001 spaces & fills them with undefined.
I dont want that as my keys could be very large ( around 1 mill).
I could use it as A["dummy1000"]=obj but I dont want to use this dirty method.
Anyway of doing it elegantly & with ease too ?

Doing A[1000] = 1 doesn't create an array with 1000 elements. It creates an array object whose length attribute is 1001, but this is only because the length attribute in JavaScript arrays is defined as the maximum index + 1.
The reason it works like this is so you can do for(var i = 0; i < A.length; i++).
I see you're confused about the allocation of the array. To you it looks like JavaScript has filled the elements with undefined - actually there isn't anything there, but if you try to access any element in an array that hasn't been defined you get undefined.

Create a hash code from the key, and use that as index. Make the hash code limited to a small range, so that you get a reasonably small array of buckets.
Something like:
function HashMap() {
// make an array of 256 buckets
this.buckets = [];
for (var i = 0; i < 256; i++) this.buckets.push([]);
}
HashMap.prototype.getHash = function(key) {
return key % 256;
}
HashMap.prototype.getBucket = function(key) {
return this.buckets[this.getHash(key)];
}
HashMap.prototype.getBucketItem = function(bucket, key) {
for (var i = 0; i < bucket.length; i++) {
if (bucket[i].key == key) return i:
}
return -1;
}
HashMap.prototype.setItem = function(key, value) {
var bucket = this.getBucket(key);
var index = this.getBucketItem(bucket, key);
if (index == -1) {
bucket.push({ key: key, value: value });
} else {
bucket[index].value = value;
}
}
HashMap.prototype.getItem = function(key) {
var bucket = this.getBucket(key);
var index = this.getBucketItem(bucket, key);
if (index == -1) {
return null;
} else {
return bucket[index].value;
}
}
Disclaimer: Code is not tested.

Related

Difficulty with object function

Write a function named "indexed_kvs" that doesn't take any parameters and returns a new key-value store containing the integers from 0 to 47 as values each stored at a key which is a string containing the digits of the integer. For example the key-value "0":0 will be in your returned key-value store (include both 0 and 47 in your list) (My code below)
function indexed_kvs(){
var d = (dict = []);
for (var i of Array(47).keys()) {
d = dict.push(i);
}
return d;
}
I keep on returning the input 47, instead of the keys and values ranging from 0 to 47. How can I fix this?
Just use a simple while loop and iterate from the end or use a for loop.
function indexed_kvs() {
var object = {},
i = 48;
while (i--) object[i] = i;
return object;
}
console.log(indexed_kvs());
A shorter approach by generating an array and then create an object of the array.
function indexed_kvs() {
return Object.assign({}, [...Array(48).keys()]);
}
console.log(indexed_kvs());
This should work for you.
function makeKeys() {
var d = {};
for (var i = 0; i < 48; i++) {
d[i] = i;
}
return d;
}
console.log(makeKeys())

Word frequency for array of key/values on javascript

I'm trying to implement a piece of code on javascript to analyse word/frequency on a given string. My objective is to return a array as the following:
[{text: firstword, size:3 },{text:secondword , size:5 },{text: nword, size: 1},...]
I implemented the following code but I'm running out of memory, so I don't really know if its ok or not.
function wordFrequency(txt){
var wordArray = txt.split(/[ .?!,*'"]/);
var newArray = [];
$.each(wordArray, function (ix, word) {
if (newArray.length >= 1){
newArray.some(function (w){
if (w.text === word){
w.size++;
} else {
newArray.push({text: word, size: 1});
}
});
} else {
newArray.push({text: word, size: 1});
}
});
return newArray;
}
Array.prototype.some expects the given callback to return true or false and returns true as soon as your callback returns true for a given element, otherwise it returns false.
So some iterates over all elements, with your given callback, and your callback checks if the given element text equals the search word and if not adds a new object. Introducing a new element the some function can iterate over.
So to make this clear, for every word thats in the newArray before the word you're searching, you're adding a new object containing your word.
Suppose your newArray looks like this:
[{word:"test"},{word:"another"},{word:"one"},{word:"more"}]
after calling your function for the word even it looks like this:
[{word:"test"},{word:"another"},{word:"one"},{word:"more"},{word:"even"},{word:"even"},{word:"even"},{word:"even"}]
Using Array.prototype.filter would be the better approach here, finding you the matching element, note that I also replaced $.each with Array.prototype.forEach:
function wordFrequency(txt){
var wordArray = txt.split(/[ .?!,*'"]/);
var newArray = [], wordObj;
wordArray.forEach(function (word) {
wordObj = newArray.filter(function (w){
return w.text == word;
});
if (wordObj.length) {
wordObj[0].size += 1;
} else {
newArray.push({text: word, size: 1});
}
});
return newArray;
}
document.write(JSON.stringify(wordFrequency("count everything, count all the words, count all the words!").sort(function(a,b){return a.size<b.size})).split("},").join("}<br/>"));
It would be simpler and far more efficient to create a direct map from word to frequency, and only afterwards convert that to your array structure. Given an array words create a map of the words:
var freq = words.reduce(function(p, c) {
p[c] = (p[c] || 0) + 1;
return p;
}, {});
and the convert that map into your array:
var array = Object.keys(freq).map(function(key) {
return { text: key, size: freq[key] };
});
To tell the frequency all you need is a hash map approach. Your algorithm is quadratic, since the some method is nested in the each method, so you're always looping over the newArray just to find an entry and increment the size.
A map approach is easily achievable using a JavaScript object. It also gives you constant look-up time, which is better performance than the nested loops approach.
Try this approach instead:
function wordFrequency(txt){
var wordArray = txt.split(/[ .?!,*'"]/);
var map = {};
$.each(wordArray, function(ix, word) {
// skip empty results
if (!word.length) {
return;
}
// add word to map
if (!map[word]) {
map[word] = 0;
}
map[word]++;
});
return map;
}
To use the function:
var text = "hello!world*hello foo 'bar'foo";
var result = wordFrequency(text);
// iterate over results
Object.keys(result).forEach(function(w) {
console.log(w + ": " + result[w]);
});
// or use for...in
for (var w in result) {
console.log(w + ": " + result[w]);
}
If you really wanted to, you could then map the result into your desired array format with text and size properties:
var mappedResult = Object.keys(result).map(function(w) {
return { text: w, size: result[w] };
});
console.log(mappedResult);
Also, depending on your target browsers, you might consider using the array forEach instead of the jQuery $.each, similar to what I did with the Object.keys portion.
Here's the JSBin example.
You would probably want to avoid any iterations on duplicate elements and keep your results array unique. Since any of the iterators of Array.prototype will include each of the elements, they might not be the ideal solution for this. Sometimes plain old loops do the job best ...
(You may also want to expressively escape any special characters in your regular expression).
function wordFrequency(txt) {
var words = txt.split(/[ \.\?!,\*'"]+/),
seen = [];
for (var i = 0; i < words.length; i++) {
var w = words[i],
found = false;
for (var j = 0; j < seen.length; j++) {
if (w === seen[j].text) {
seen[j].size++;
found = true;
break;
}
}
if (!found) seen.push( { text: w, size: 1 } );
}
return seen;
}
(Note that the inner for-loop isn't visited for the first word, so the first word will be pushed to the seen-stack and the inner for-loop will start with the second word compared to the first one. Only words that we haven't seen already are added to the seen-stack, making it an array of unique elements.)
And here is the equivalent using Array.prototype.forEach() and Array.prototype.indexOf(), but we have to add another intermediate results stack for the latter one. So we'll have to add another iteration to produce the final result. (We wouldn't have to do this using Array.prototype.findIndex(), but this is not a standard method.)
function wordFrequency2(txt) {
var words = txt.split(/[ \.\?!,\*'"]+/),
seen = [],
freq = [];
// get frequencies
words.forEach(function (w) {
var idx = seen.indexOf(w);
if (idx >= 0) {
freq[idx]++;
}
else {
seen.push(w);
freq.push(1);
}
});
// produce the results array
var r = [];
seen.forEach(function (w, idx) {
r.push( { text: w, size: freq[idx] } );
});
return r;
}
Putting optimization into account, the first version using explicit loops will be probably performing faster ...
var words = (function(){
var sWords = document.body.innerText.toLowerCase().trim().replace(/[,;.]/g,'').split(/[\s\/]+/g).sort();
var iWordsCount = sWords.length; // count w/ duplicates
// array of words to ignore
var ignore = ['and','the','to','a','of','for','as','i','with','it','is','on','that','this','can','in','be','has','if'];
ignore = (function(){
var o = {}; // object prop checking > in array checking
var iCount = ignore.length;
for (var i=0;i<iCount;i++){
o[ignore[i]] = true;
}
return o;
}());
var counts = {}; // object for math
for (var i=0; i<iWordsCount; i++) {
var sWord = sWords[i];
if (!ignore[sWord]) {
counts[sWord] = counts[sWord] || 0;
counts[sWord]++;
}
}
var arr = []; // an array of objects to return
for (sWord in counts) {
arr.push({
text: sWord,
frequency: counts[sWord]
});
}
// sort array by descending frequency | http://stackoverflow.com/a/8837505
return arr.sort(function(a,b){
return (a.frequency > b.frequency) ? -1 : ((a.frequency < b.frequency) ? 1 : 0);
});
}());
(function(){
var iWordsCount = words.length; // count w/o duplicates
for (var i=0; i<iWordsCount; i++) {
var word = words[i];
console.log(word.frequency, word.text);
}
}());

Delete JSON items based on array of allowed items

I have an array of allowedFields based on the names of the keys from a JSON array generated from a form.
A number of the retrieved fields are not required at this stage and therefore should not go through the validation process, therefore I want to match the values of the JSON array with the values of the allowedFields array
Returned JSON from form
{"reference":"sdfsdfsdfsd",
"start_date":"04/22/2014",
"end_date":"05//2014",
"status":"1","frequency":"M",
"day":"sat",
"contract_type":"S",
"notice_period":"1M"}
allowedFields = array(
reference,
start_date,
end_date,
contract_type
)
Basically I need to strip out any fields that are not listed in the allowedFields javascript array
1) Parse the JSON to an object.
var obj = JSON.parse(json);
2) Ensure that you've defined your array correctly.
var allowedFields = ['reference','start_date','end_date','contract_type'];
3) Loop over the object and if the key is not in the array delete it.
for (var k in obj) {
if (allowedFields.indexOf(k) < 0) delete obj[k];
}
4) Stringify your object back to JSON.
var str = JSON.stringify(obj);
Output
{"reference":"sdfsdfsdfsd","start_date":"04/22/2014","end_date":"05//2014","contract_type":"S"}
Fiddle
var all = {"reference":"sdfsdfsdfsd",
"status":"1"};
var allowedFields = ['reference']; // note quote marks to create strings
function filter(data, allowed) {
var filtered = {};
for(var id=0; id < allowed.length; ++id) {
var allowedField = allowed[id];
if(data.hasOwnProperty(allowedField)) {
filtered[allowedField] = data[allowedField];
}
}
return filtered;
}
console.log(filter(all, allowedFields));
>> [object Object] {
>> reference: "sdfsdfsdfsd"
>> }
Demo
underscore.js solution:
_.pick(obj,allowedFields)
http://underscorejs.org/#pick
demo
There is also _.omit(obj,string|string[]) that does the opposite.
underscore.js is extremely useful and I use it quite a bit, but you can also pick just the tools you need and include those in your code. The library is quite optimized and there is no need to write your own.
Here is the implementation (from here)
_.pick = function(obj, iterator, context) {
var result = {};
if (_.isFunction(iterator)) {
for (var key in obj) {
var value = obj[key];
if (iterator.call(context, value, key, obj)) result[key] = value;
}
} else {
var keys = concat.apply([], slice.call(arguments, 1));
for (var i = 0, length = keys.length; i < length; i++) {
var key = keys[i];
if (key in obj) result[key] = obj[key];
}
}
return result;
};

javascript array with numeric index without undefineds

suppose I do..
var arr = Array();
var i = 3333;
arr[i] = "something";
if you do a stringify of this array it will return a string with a whole bunch of undefined numeric entries for those entries whose index is less than 3333...
is there a way to make javascript not do this?
I know that I can use an object {} but I would rather not since I want to do array operations such as shift() etc which are not available for objects
If you create an array per the OP, it only has one member with a property name of "333" and a length of 334 because length is always set to be at least one greater than the highest index. e.g.
var a = new Array(1000);
has a length of 1000 and no members,
var a = [];
var a[999] = 'foo';
has a length of 1000 and one member with a property name of "999".
The speedy way to only get defined members is to use for..in:
function myStringifyArray(a) {
var s = [];
var re = /^\d+$/;
for (var p in a) {
if (a.hasOwnProperty(p) && re.test(p)) {
s.push(a[p]);
}
}
return '' + s;
}
Note that the members may be returned out of order. If that is an issue, you can use a for loop instead, but it will be slower for very sparse arrays:
function myStringifyArray(a) {
var s = [];
var re = /^\d+$/;
for (var i=0, iLen=a.length; i<iLen; i++) {
if (a.hasOwnProperty(i)) {
s.push(a[i]);
}
}
return '' + s;
}
In some older browsers, iterating over the array actually created the missing members, but I don't think that's in issue in modern browsers.
Please test the above thoroughly.
The literal representation of an array has to have all the items of the array, otherwise the 3334th item would not end up at index 3333.
You can replace all undefined values in the array with something else that you want to use as empty items:
for (var i = 0; i < arr.length; i++) {
if (typeof arr[i] == 'undefined') arr[i] = '';
}
Another alternative would be to build your own stringify method, that would create assignments instead of an array literal. I.e. instead of a format like this:
[0,undefined,undefined,undefined,4,undefined,6,7,undefined,9]
your method would create something like:
(function(){
var result = [];
result[0] = 0;
result[4] = 4;
result[6] = 6;
result[7] = 7;
result[9] = 9;
return result;
}())
However, a format like that is of course not compatible with JSON, if that is what you need.

JavaScript: convert objects to array of objects

I have thousands of legacy code that stores array information in a non array.
For example:
container.object1 = someobject;
container.object2 = someotherobject;
container.object3 = anotherone;
What I want to have is:
container.objects[1], container.objects[2], container.objects[3] etc.
The 'object' part of the name is constant. The number part is the position it should be in the array.
How do I do this?
Assuming that object1, object2, etc... are sequential (like an array), then you can just iterate through the container object and find all the sequential objectN properties that exist and add them to an array and stop the loop when one is missing.
container.objects = []; // init empty array
var i = 1;
while (container["object" + i]) {
container.objects.push(container["object" + i]);
i++;
}
If you want the first item object1 to be in the [1] spot instead of the more typical [0] spot in the array, then you need to put an empty object into the array's zeroth slot to start with since your example doesn't have an object0 item.
container.objects = [{}]; // init array with first item empty as an empty object
var i = 1;
while (container["object" + i]) {
container.objects.push(container["object" + i]);
i++;
}
An alternate way to do this is by using keys.
var unsorted = objectwithobjects;
var keys = Object.keys(unsorted);
var items = [];
for (var j=0; j < keys.length; j++) {
items[j] = unsorted[keys[j]];
}
You can add an if-statement to check if a key contains 'object' and only add an element to your entry in that case (if 'objectwithobjects' contains other keys you don't want).
That is pretty easy:
var c = { objects: [] };
for (var o in container) {
var n = o.match(/^object(\d+)$/);
if (n) c.objects[n[1]] = container[o];
}
Now c is your new container object, where c.object[1] == container.object1

Categories

Resources