Data structure to store unique values in javascript - javascript

What is the best data type to store unique values only?
an array can have duplicates
[one, one, two]
and an object (? maybe wrong terminology) have unnecessary values for my current case
{one: something, two: something, three: something}
Shortly, I need something like this:
{one, two, three}
I am not sure what it is called, or if it does exist in js. Needing some enlightment.

You mean a structure called Set, and in the current version of ECMAScript there's no such structure. It will be standarized in the next version, however it's available now in some browsers.
You can emulate set using object, but as you said that also involves unnecessary values. If you don't want to care about them, you can use a library that emulates Set, like http://collectionsjs.com/

The most common way to solve this is to use an array, and just check if it already has the value you want to insert, that way it contains only unique values.
if ( arr.indexOf(value) == -1 ) arr.push(value);

In addition to obvious ways you can always create you own data structure on top of array if you need some more advanced functionality. For example:
function UArray(val) {
this._values = [];
if (typeof val !== 'undefined') {
this.set(val);
}
}
UArray.prototype.set = function(values) {
this._values = this._values.concat(values).filter(function(el, i, arr) {
return arr.indexOf(el) === i;
});
};
UArray.prototype.get = function() {
return this._values;
}
var uarr = new UArray();
uarr.set(['one', 'one', 'two']);
alert( uarr.get() );
uarr.set('two');
uarr.set(['three', 'one']);
alert( uarr.get() );
Such a custom data structure could be extended with additional necessary methods, i.e.:
remove to remove specific item
find to find item's index or -1 if not found,
etc.

Related

Is there a method to check if an array has ANY value in it?

Say I want to have an if statement to test if an array is empty or not,
Looking it up I have found no way to check besides array[0], which in frameworks like React and Angular give errors on the array's item not existing.
let arr = [];
if (!arr[0]) {
arr.map(val=>{
return <div>{val.name}</div>
});
}
In this example I want it to Display the JSX and if not, do not display any JSX. But doing this throws an error. Is there anyway to do this? preferably without any outside libraries.
edit: "added 'this' after 'to do'"
This checks whether the variable is defined and whether it contains anything (although in this case you'd probably not need the first check):
if ( arr && arr.length )
Also, don't forget to return arr.map(...
If you're not even certain your variable even is an array, this is what I would do:
if (Array.isArray(arr) && arr.length) { /*...*/ }
For your special case you probably don't even need the 2nd check, though, because all of the Array iterators don't run even once if the Array they are called on is empty.
Example:
const arr = [];
console.log(arr.map(Number));
console.log(arr.some(parseInt));
console.log(arr.every(parseInt));
console.log(arr.filter(Number));
None of the methods will throw an error provide arr is an array.

Non case-sensitive sorting in dojo dgrid

Is it possible to sort without case-sensitivity?
For instance, sorts by default show up like this:
Awesomeman
adam
beyonce
but, I'd like to sort like:
adam
Awesomeman
beyonce
Is it possible to override the sensitivity easily? From what I can tell the grid inherits from OnDemandGrid and OnDemandList, which both inherit from Grid and List. For my store, I am using Memory wrapped in Observable.
As of now, I'm trying to overwrite _setSort in List.js, however that's not working. Anyone out there familiar with these frameworks?
There are potentially 2 ways of solving this:
On the grid end, by handling and canceling the dgrid-sort event
On the store end, by extending query to coerce sort into doing what you want (preferred)
First, the dgrid-sort version:
grid.on('dgrid-sort', function (event) {
// Cancel the event to prevent dgrid's default behavior which simply
// passes the sort criterion through to the store and updates the UI
event.preventDefault();
// sort is an array as expected by the store API, but dgrid's UI only sorts one field at a time
var sort = event.sort[0];
grid.set('sort', function (a, b) {
var aValue = a[sort.attribute].toLowerCase();
var bValue = b[sort.attribute].toLowerCase();
if (aValue === bValue) {
return 0;
}
var result = aValue > bValue ? 1 : -1;
return result * (sort.descending ? -1 : 1);
});
// Since we're canceling the event, we need to update the UI ourselves;
// the `true` tells it to also update dgrid's internal representation
// of the sort setting, so that toggling between asc/desc will still work
grid.updateSortArrow(event.sort, true);
});
While this works for handling when the user clicks in header cells, it will not take effect for programmatic set('sort') calls, or the initial setting of sort in the object passed to the Grid constructor, which could be problematic.
Since sorting is ultimately a store concern, addressing it on the store end is really the preferable solution. Admittedly dojo/store/Memory and namely dojo/store/util/SimpleQueryEngine doesn't make this...well...simple... but one thing to note about SimpleQueryEngine is that if you pass a function via queryOptions.sort rather than an array, it will be applied verbatim as the sort function to use.
This means we can take the incoming sort array that dgrid will set, write our own version of SimpleQueryEngine's default sort function while also accounting for case-insensitivity, and store that in queryOptions.sort for the inherited call:
var CIMemory = declare(Memory, {
query: function (query, queryOptions) {
var sort = queryOptions && queryOptions.sort;
if (sort) {
// Replace sort array with a function equivalent that performs
// case-insensitive sorting
queryOptions.sort = function (a, b) {
for (var i = 0; i < sort.length; i++) {
var aValue = a[sort[i].attribute].toLowerCase();
var bValue = b[sort[i].attribute].toLowerCase();
if (aValue !== bValue) {
var result = aValue > bValue ? 1 : -1;
return result * (sort[i].descending ? -1 : 1);
}
}
return 0;
}
}
return this.inherited(arguments);
}
});
Using this in place of dojo/store/Memory will cause all sorts to be case-insensitive.
Note that I took a couple of shortcuts over SimpleQueryEngine's sort function (checking for null/undefined and coercing values to primitives). Alter the sort function as necessary if you need to worry about either of those things.
Okay, for lack of a better solution, I found that I had to create a custom version of dojo's SimpleQueryEngine, adding .toLowerCase() on both of the values here. The reason it couldn't be simply changed was because it happens inside of an internal function (inside of another function) so it was easier to make another version entirely.
Then, when creating Memory, passing in the query engine like so:
new Memory({ queryEngine : customEngine });
and it seems to work. If there's a cleaner solution please share bc I hate this one :)

Hashing JavaScript objects

I have a function that receives a list of JS objects as an argument. I need to store information about those objects in a private variable for future reference. I do not want to stuff a property into the objects themselves, I just want to keep it out of band in a dictionary. I need to be able to lookup metadata for an object in sub-linear time.
For this I need a hash function such that, for any two objects o1 and o2,
hash(o1) !== hash(o2) whenever o1 !== o2.
A perfect example of such a hash function would be the memory address of the object, but I don't think JS exposes that. Is there a way?
Each object reference is different. Why not push the object onto an array? Traversing the array looking for an object reference might still perform better than inspecting each object in a recursive manor to generate a hash key.
function Dictionary() {
var values = [];
function contains(x) {
var i = values.length;
while(i--) {
if (values[i] === x) {
return true;
}
}
return false;
}
function count() {
return values.length;
}
function get(i) {
return (i >= 0 && i < values.length) ? values[i] : null;
}
function set(o) {
if (contains(o)) {
throw new Error("Object already exists in the Dictionary");
}
else {
return values.push(o) - 1;
}
}
function forEach(callback, context) {
for (var i = 0, length = values.length; i < length; i++) {
if (callback.call(context, values[i], i, values) === false) {
break;
}
}
}
return {
get: get,
set: set,
contains: contains,
forEach: forEach,
count: count
};
}
And to use it:
var objects = Dictionary();
var key = objects.set({});
var o = objects.get(key);
objects.contains(key); // returns true
objects.forEach(function(obj, key, values) {
// do stuff
}, this);
objects.count(); // returns 1
objects.set(o); // throws an error
To store metadata about objects, you can use an WeakMap:
WeakMaps are key/value maps in which keys are objects.
Note that this API is still experimental and thus not widely supported yet (see support table). There is a polyfill implementation which makes use of defineProperty to set GUIDs (see details here).
Javascript does not provide direct access to memory (or to the file system for that matter).
You'd probably just want to create your properties/variables within the analysis (hash) function, and then return them to where the function was called from to be stored/persisted for later reference.
Thanks everyone who chipped in to reply. You all have convinced me that what I want to do is currently not possible in JavaScript.
There seem to be two basic compromises that someone with this use case can chose between:
Linear search using ===
=== appears to be the only built-in way to distinguish between two identically-valued objects that have different references. (If you had two objects, o1 and o2, and did a deep comparison and discovered that they were value-identical, you might still want to know if they're reference-identical. Besides === you could do something weird like add a property to o1 and see if showed up in o2).
Add a property to the object.
I didn't like this approach because there's no good reason why I should have to expose this information to the outside world. However, a colleague tipped me off to a feature that I didn't know about: Object.defineProperty. With this, I can alleviate my main concerns: first, that my id would show up, unwanted, during object enumeration, and second, that someone could inadvertently alter my id if there were to be a namespace collision.
So, in case anyone comes here wanting the same thing I wanted, I'm putting it up there for the record that I'm going to add a unique id using Object.defineProperty.

javascript: Only return if not false

Scenario: I'm searching for a specific object in a deep object. I'm using a recursive function that goes through the children and asks them if I'm searching for them or if I'm searching for their children or grandchildren and so on. When found, the found obj will be returned, else false. Basically this:
obj.find = function (match_id) {
if (this.id == match_id) return this;
for (var i = 0; i < this.length; i++) {
var result = this[i].find(match_id);
if (result !== false) return result;
};
return false;
}​
i'm wondering, is there something simpler than this?:
var result = this[i].find(match_id);
if (result) return result;
It annoys me to store the result in a variable (on each level!), i just want to check if it's not false and return the result. I also considered the following, but dislike it even more for obvious reasons.
if (this[i].find(match_id)) return this[i].find(match_id);
Btw I'm also wondering, is this approach even "recursive"? it isn't really calling itself that much...
Thank you very much.
[edit]
There is another possibility by using another function check_find (which just returns only true if found) in the if statement. In some really complicated cases (e.g. where you don't just find the object, but also alter it) this might be the best approach. Or am I wrong? D:
Although the solution you have is probably "best" as far as search algorithms go, and I wouldn't necessarily suggest changing it (or I would change it to use a map instead of an algorithm), the question is interesting to me, especially relating to the functional properties of the JavaScript language, and I would like to provide some thoughts.
Method 1
The following should work without having to explicitly declare variables within a function, although they are used as function arguments instead. It's also quite succinct, although a little terse.
var map = Function.prototype.call.bind(Array.prototype.map);
obj.find = function find(match_id) {
return this.id == match_id ? this : map(this, function(u) {
return find.call(u, match_id);
}).filter(function(u) { return u; })[0];
};​
How it works:
We test to see if this.id == match_id, if so, return this.
We use map (via Array.prototype.map) to convert this to an array of "found items", which are found using the recursive call to the find method. (Supposedly, one of these recursive calls will return our answer. The ones which don't result in an answer will return undefined.)
We filter the "found items" array so that any undefined results in the array are removed.
We return the first item in the array, and call it quits.
If there is no first item in the array, undefined will be returned.
Method 2
Another attempt to solve this problem could look like this:
var concat = Function.prototype.call.bind(Array.prototype.concat),
map = Function.prototype.call.bind(Array.prototype.map);
obj.find = function find(match_id) {
return (function buildObjArray(o) {
return concat([ o ], map(o, buildObjArray));
})(this).filter(function(u) { return u.id == match_id })[0];
};
How it works:
buildObjArray builds a single, big, 1-dimensional array containing obj and all of obj's children.
Then we filter based on the criteria that an object in the array must have an id of match_id.
We return the first match.
Both Method 1 and Method 2, while interesting, have the performance disadvantage that they will continue to search even after they've found a matching id. They don't realize they have what they need until the end of the search, and this is not very efficient.
Method 3
It is certainly possible to improve the efficiency, and now I think this one really gets close to what you were interested in.
var forEach = Function.prototype.call.bind(Array.prototype.forEach);
obj.find = function(match_id) {
try {
(function find(obj) {
if(obj.id == match_id) throw this;
forEach(obj, find);
})(obj);
} catch(found) {
return found;
}
};​
How it works:
We wrap the whole find function in a try/catch block so that once an item is found, we can throw and stop execution.
We create an internal find function (IIFE) inside the try which we reference to make recursive calls.
If this.id == match_id, we throw this, stopping our search algorithm.
If it doesn't match, we recursively call find on each child.
If it did match, the throw is caught by our catch block, and the found object is returned.
Since this algorithm is able to stop execution once the object is found, it would be close in performance to yours, although it still has the overhead of the try/catch block (which on old browsers can be expensive) and forEach is slower than a typical for loop. Still these are very small performance losses.
Method 4
Finally, although this method does not fit the confines of your request, it is much, much better performance if possible in your application, and something to think about. We rely on a map of ids which maps to objects. It would look something like this:
// Declare a map object.
var map = { };
// ...
// Whenever you add a child to an object...
obj[0] = new MyObject();
// .. also store it in the map.
map[obj[0].id] = obj[0];
// ...
// Whenever you want to find the object with a specific id, refer to the map:
console.log(map[match_id]); // <- This is the "found" object.
This way, no find method is needed at all!
The performance gains in your application by using this method will be HUGE. Please seriously consider it, if at all possible.
However, be careful to remove the object from the map whenever you will no longer be referencing that object.
delete map[obj.id];
This is necessary to prevent memory leaks.
No there is no other clear way, storing the result in a variable isn't that much trouble, actually this is what variables are used for.
Yes, that approach is recursive:
you have the base case if (this.id==match_id) return this
you have the recursive step which call itself obj.find(match_id) { ... var result = this[i].find(match_id); }
I don't see any reason, why storing the variable would be bad. It's not a copy, but a reference, so it's efficient. Plus the temporary variable is the only way, that I can see right now (I may be wrong, though).
With that in mind, I don't think, that a method check_find would make very much sense (it's most probably basically the same implementation), so if you really need this check_find method, I'd implement it as
return this.find(match_id) !== false;
Whether the method is recursive is hard to say.
Basically, I'd say yes, as the implementations of 'find' are all the same for every object, so it's pretty much the same as
function find(obj, match_id) {
if (obj.id == match_id) return obj;
for (var i = 0; i < obj.length; ++i) {
var result = find(obj[i], match_id);
if (result !== false) return result;
}
}
which is definitely recursive (the function calls itself).
However, if you'd do
onesingleobjectinmydeepobject.find = function(x) { return this; }
I'm not quite sure, if you still would call this recursive.

Better Understanding Javascript by Examining jQuery Elements

Because jQuery is a widely used and mature collaborative effort, I can't help but to look at its source for guidance in writing better Javascript. I use the jQuery library all the time along with my PHP applications, but when I look under the hood of this rather sophisticated library I realize just how much I still don't understand about Javascript. Lo, I have a few questions for the SO community. First of all, consider the following code...
$('#element').attr('alt', 'Ivan is SUPER hungry! lolz');
vs
$('#element').attr({'alt': 'Ivan is an ugly monster! omfgz'});
Now, is this to say that the attr() method was designed to accept EITHER an attribute name, an attribute name and a value, or a pair-value map? Can someone give me a short explanation of what a map actually is and the important ways that it differs from an array in Javascript?
Moving on, the whole library is wrapped in this business...
(function(window, undefined) { /* jQuery */ })(window);
I get that the wrapped parentheses cause a behavior similar to body onLoad="function();", but what is this practice called and is it any different than using the onLoad event handler? Also, I can't make heads or tails of the (window) bit there at the end. What exactly is happening with the window object here?
Am I wrong in the assessment that objects are no different than functions in Javascript? Please correct me if I'm wrong on this but $() is the all encompassing jQuery object, but it looks just like a method. Here's another quick question with a code example...
$('#element').attr('alt', 'Adopt a Phantom Cougar from Your Local ASPCA');
... Should look something like this on the inside (maybe I'm wrong about this)...
function $(var element = null) {
if (element != null) {
function attr(var attribute = null, var value = null) {
/* stuff that does things */
}
}
}
Is this the standing procedure for defining objects and their child methods and properties in Javascript? Comparing Javascript to PHP, do you use a period . the same way you would use -> to retrieve a method from an object?
I apologize for this being a bit lengthy, but answers to these questions will reveal a great deal to me about jQuery and Javascript in general. Thanks!
1. Method overloading
$('#element').attr('alt', 'Ivan is SUPER hungry! lolz');
vs
$('#element').attr({'alt': 'Ivan is an ugly monster! omfgz'});
var attr = function (key, value) {
// is first argument an object / map ?
if (typeof key === "object") {
// for each key value pair
for (var k in key) {
// recursively call it.
attr(k, key[k]);
}
} else {
// do magic with key and value
}
}
2. Closures
(function(window, undefined) { /* jQuery */ })(window);
Is not used as an onload handler. It's simply creating new scope inside a function.
This means that var foo is a local variable rather then a global one. It's also creating a real undefined variable to use since Parameters that are not specified passes in undefined
This gaurds againts window.undefined = true which is valid / allowed.
the (window) bit there at the end. What exactly is happening with the window object here?
It's micro optimising window access by making it local. Local variable access is about 0.01% faster then global variable access
Am I wrong in the assessment that objects are no different than functions in Javascript?
Yes and no. All functions are objects. $() just returns a new jQuery object because internally it calls return new jQuery.fn.init();
3. Your snippet
function $(var element = null) {
Javascript does not support default parameter values or optional parameters. Standard practice to emulate this is as follows
function f(o) {
o != null || (o = "default");
}
Comparing Javascript to PHP, do you use a period . the same way you would use -> to retrieve a method from an object?
You can access properties on an object using foo.property or foo["property"] a property can be any type including functions / methods.
4. Miscellanous Questions hidden in your question
Can someone give me a short explanation of what a map actually is and the important ways that it differs from an array in Javascript?
An array is created using var a = [] it simply contains a list of key value pairs where all the keys are positive numbers. It also has all the Array methods. Arrays are also objects.
A map is just an object. An object is simply a bag of key value pairs. You assign some data under a key on the object. This data can be of any type.
For attr, if you give an object instead of a key value pair it will loop on each property.
Look for attr: in jQuery's code, then you'll see it use access. Then look for access: and you will see there is a check on the type of key if it is an object, start a loop.
The wrapping in a function, is to prevent all the code inside to be accessed from outside, and cause unwanted problems. The only parameters that are passed are window that allow to set globals and access the DOM. The undefined I guess it is to make the check on this special value quicker.
I read sometimes jQuery but I didn't start with it, may be you should get some good books to make you an idea first of what some advanced features Javascript has, and then apply your knowledge to the specifics of jQuery.
1 - Yes attr can accept a attribute name for getting a value, a name and a value for setting one value or a map of attribute names and values for settings more than one attribute
2 - A map is basically a JavaScript object e.g:
var map = {
'key1' : 'value1',
'key2' : 'value2'
};
3 - (function(window, undefined) { /* jQuery */ })(window); is something called an anonymous function as it doesn't have a name. In this case it also executes straight away.
A simple example would be:
function test(){
...
}
test();
//As an anonymous function it would be:
(function(){
...
}();
//And it you wanted to pass variables:
function test(abc){
...
}
test(abc);
//As an anonymous function it would be:
(function(abc){
...
}(abc);
this would make it different to the load event, as it is a function not an event.
4 - window is passed as a variable, as it is used internally within jQuery
5 - Objects and functions the same, as everything in JavaScript is an object. jQuery does something like this:
var obj = {
"init" : function(){
}
}
6 - Yes you can use . to retrieve a value on an object but you can also use [] e.g:
var map = {
"test" : 1
}
map.test //1
map["test"] //1
I hope this answers your many questions, let me know if I've missed anything out.
jQuery 1.6.1
The test is typeof key === "object"
if that is true, then you passed a { .... }
jQuery.fn.extend({
attr: function( name, value ) {
return jQuery.access( this, name, value, true, jQuery.attr );
},
// Mutifunctional method to get and set values to a collection
// The value/s can be optionally by executed if its a function
access: function( elems, key, value, exec, fn, pass ) {
var length = elems.length;
// Setting many attributes
if ( typeof key === "object" ) {
for ( var k in key ) {
jQuery.access( elems, k, key[k], exec, fn, value );
}
return elems;
}
// Setting one attribute
if ( value !== undefined ) {
// Optionally, function values get executed if exec is true
exec = !pass && exec && jQuery.isFunction(value);
for ( var i = 0; i < length; i++ ) {
fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
}
return elems;
}
// Getting an attribute
return length ? fn( elems[0], key ) : undefined;
},

Categories