Use arrays as keys for a dict - javascript

Note:
Asking about alternative methods for performing a task is not a matter of opinion.
Asking about pros and cons of such methods is not a matter of opinion.
This is what I am asking.
Asking which method is preferable, based on such pros and cons, is a matter of opinion.
This is not what I am asking.
I mean to use a dict (or a similar object) in Google apps script, with arrays as keys.
These (I didn't know) are converted to strings when used as dict keys.
The way I wrote my code, for what I needed, was working.
After I added some functionality, the limitations surfaced.
For instance, I want keys to be something like [<color>,<number>].
Then I mean to do
for (key in mydict) {
var c = key[0];
var n = key[1];
... work separately with c as a string and n as a number
}
I guess I could split key at the comma, and convert n to int, but if there is a less cumbersome alternative, I would go for that.
Plus, in a more general case the conversion of key back to its original type may be more involved.
One solution I found is with WeakMap.
I am currently trying it.
Regardless the fact it may work, is there any alternative?
What are possible pros and cons of those other options?
It would be very good to know before rewriting all code.

Use nested objects.
mydict = {
"color1": {
1: <somevalue>,
5: <othervalue>
},
"color2": {
3: <value3>,
10: <value4>
}
};
Then use nested loops:
Object.entries(mydict).forEach(([c, item]) =>
Object.entries(item).forEach([n, value]) => {
// do something with c, n, value
})
);

One simple option is
for (keystr in mydict) {
var key = keystr.split(',');
var c = key[0];
var n = Number(key[1]);
... work separately with c as a string and n as a number
}
which works as long as none of the elements in the keys contain ','.

Related

Is there a way to return the rest of a JavaScript array

Is there a way to return the rest of an array in JavaScript i.e the portion of the array that consists of all elements but the first element of the array?
Note: I do not ask for returning a new array e.g. with arr.slice(1) etc. and I do not want to chop off the first element of the array e.g. with arr.shift().
For example, given the array [3, 5, 8] the rest of the array is [5, 8] and if the rest of the array is changed, e.g. by an assignment (a destructive operation), the array also changes. I just figured out that as a test that proves the rest is the rest of the array but not a new array consists of the rest of the elements of the array.
Note: The following code example is to describe what I want, but not specifically what I want to do (i.e. not the operations I want to perform). What I want to do is in the every algorithm at the bottom.
var arr = [3, 5, 8];
var rest = rest(arr); // rest is [5, 8]
rest.push(13); // rest is [5, 8, 13] and hence the arr is [3, 5, 8, 13]
An example I possibly need this and I would want to have it is following algorithm and many other I am writing in that GitHub organization, in both of which I use always arr.slice(1):
function every(lst, f) {
if (lst.length === 0) {
return false;
} else {
if (f(lst[0]) === true) {
return every(lst.slice(1), f);
} else {
return false;
}
}
}
I think having what I ask for instead of arr.slice(1) would keep the memory usage of such algorithms and retain the recursive-functional style I want to employ.
No, this is generally not possible. There are no "views on" or "pointers to" normal arrays1.
You might use a Proxy to fake it, but I doubt this is a good idea.
1: It's trivial to do this on typed arrays (which are views on a backing buffer), but notice that you cannot push to them.
I possibly need this and I would want to have it for recursive-functional style algorithms where I currently use arr.slice(1) but would prefer to keep memory usage low
Actually, all of these implementations do have low memory usage - they don't allocate more memory than the input. Repeatedly calling slice(1) does lead to high pressure on the garbage collector, though.
If you were looking for better efficiency, I would recommend to
avoid recursion. JS engines still didn't implement tail recursion, so recursion isn't cheap.
not to pass around (new copies of) arrays. Simply pass around an index at which to start, e.g. by using an inner recursive function that closes over the array parameter and accesses array[i] instead of array[0]. See #Pointy's updated answer for an example.
If you were looking for a more functional style, I would recommend to use folds. (Also known as reduce in JavaScript, although you might need to roll your own if you want laziness). Implement your algorithms in terms of fold, then it's easy to swap out the fold implementation for a more efficient (e.g. iterative) one.
Last but not least, for higher efficiency while keeping a recursive style you can use iterators. Their interface might not look especially functional, but if you insist you could easily create an immutable wrapper that lazily produces a linked list.
please test this function
function rest(arr) {
var a = arr.slice(1);
a.push = function() {
for (var i = 0, l = arguments.length; i < l; i++) {
this[this.length] = arguments[i];
arr[this.length] = arguments[i];
}
return this.length;
};
return a;
}
Based on the code posted in the update to the question, it's clear why you might want to be able to "alias" a portion of an array. Here is an alternative that is more typical of how I would solve the (correctly) perceived efficiency problem with your implementation:
function every(lst, f) {
function r(index) {
if (index >= lst.length)
return true; // different from OP, but I think correct
return f(lst[index]) && r(index+1);
}
return r(0);
}
That is still a recursive solution to the problem, but no array copy is made; the array is not changed at all. The general pattern is common even in more characteristically functional programming languages (Erlang comes to mind personally): the "public" API for some recursive code is augmented by an "internal" or "private" API that provides some extra tools for keeping track of the progress of the recursion.
original answer
You're looking for Array.prototype.shift.
var arr = [1, 2, 3];
var first = arr.shift();
console.log(first); // 1
console.log(arr); // [2, 3]
This is a linear time operation: the execution cost is relative to the length of the original array. For most small arrays that does not really matter much, but if you're doing lots of such work on large arrays you may want to explore a better data structure.
Note that with ordinary arrays it is not possible to create a new "shadow" array that overlaps another array. You can do something like that with typed arrays, but for general purpose use in most code typed arrays are somewhat awkward.
The first limitation of typed arrays is that they are, of course, typed, which means that the array "view" onto the backing storage buffer gives you values of only one consistent type. The second limitation is that the only available types are numeric types: integers and floating-point numbers of various "physical" (storage) sizes. The third limitation is that the size of a typed array is fixed; you can't extend the array without creating a new backing buffer and copying.
Such limitations would be quite familiar to a FORTRAN programmer of course.
So to create an array for holding 5 32-bit integers, you'd write
var ints = new Int32Array(5);
You can put values into the array just like you put values into an ordinary array, so long as you get the type right (well close enough):
for (let i = 0; i < 5; i++)
ints[i] = i;
console.log(ints); // [0, 1, 2, 3, 4]
Now: to do what the OP asked, you'd grab the buffer from the array we just created, and then make a new typed array on top of the same buffer at an offset from the start. The offsets when doing this are always in bytes, regardless of the type used to create the original array. That's super useful for things like looking at the individual parts of a floating point value, and other "bit-banging" sorts of jobs, though of course that doesn't come up much in normal JavaScript coding. Anyway, to get something like the rest array from the original question:
var rest = new Int32Array(ints.buffer, 4);
In that statement, the "4" means that the new array will be a view into the buffer starting 4 bytes from the beginning; 32-bit integers being 4 bytes long, that means that the new view will skip the first element of the original array.
Since JavaScript can't do this, the only real solution to your problem is WebAssembly. Otherwise use Proxy.

Do arrays with gaps in their indices entail any benefits that compensate their disadvantages

In Javascript arrays may have gaps in their indices, which should not be confused with elements that are simply undefined:
var a = new Array(1), i;
a.push(1, undefined);
for (i = 0; i < a.length; i++) {
if (i in a) {
console.log("set with " + a[i]);
} else {
console.log("not set");
}
}
// logs:
// not set
// set with 1
// set with undefined
Since these gaps corrupt the length property I'm not sure, if they should be avoided whenever possible. If so, I would treat them as edge case and not by default:
// default:
function head(xs) {
return xs[0];
}
// only when necessary:
function gapSafeHead(xs) {
var i;
for (i = 0; i < xs.length; i++) {
if (i in xs) {
return xs[i];
}
}
}
Besides the fact that head is very concise, another advantage is that it can be used on all array-like data types. head is just a single simple example. If such gaps need to be considered throughout the code, the overhead should be significantly.
This is likely to come up in any language that overloads hash tables to provide something that colloquially is called an "array". PHP, Lua and JavaScript are three such languages. If you depend on strict sequential numeric array behavior, then it will be an inconvenience for you. More generally, the behavior provides conveniences as well.
Here's a classic algorithm question: to delete a member from the middle of a data structure, which data structure is "better": A linked list or an array?
You're supposed to say "linked list", because deleting a node from a linked list doesn't require you to shift the rest of the array down one index. But linked lists have other pitfalls, so is there another data structure we can use? You can use a sparse array*.
In many languages that provide this hashy type of arrays, removing any arbitrary member of the array will change the length. Unfortunately, JavaScript does not change the length, so you lose out a little there. But nevertheless, the array is "shorter", at least from the Object.keys perspective.
*Many sparse arrays are implemented using linked lists, so don't apply this too generally. In these languages, though, they're hash tables with predictable ordered numeric keys.
Of course, the question is a subjective one, but I argue that the gaps should certainly be avoided, if possible. Arrays are special Javascript objects with very specific purposes. You can totally hack on arrays, manipulate the length property, add properties with keys other than numbers (e.g myArray["foo"] = "bar"), but these mostly devolve into antipatterns. If you need some special form of pseudo-array, you can always just code it yourself with a regular object. After all, typeof [] === "object"
It's not like gaps inherently break your code, but I would avoid pursuing them intentionally.
Does that answer your question?

sort by number/id within an object in javascript

is there a way to order these (alal SELECT * FROM this ORDERBY....) in javascript. So if I just wanted to append new list elements to a DOM from an object, but wanted to control the order kind of thing.
var example = [
{id:1, data: { val1: 'val1', val2: 'val2'}, orderById: 313},
{id:2, data: { val1: 'val1', val2: 'val2'}, orderById: 312},
];
var buildString = '';
for ( var i = 0; i < example.length; i++ ) {
// wondering if there is another approach so that i could control the order as I build the string
buildString += "<li class='example-class'>" + example[i].data.val1 + "</li>";
}
$("#some-ul").html('').append(buildString);
so if there is a totally better approach to accomplish this idea I am interested in learning...
Arrays have a method called sort which does exactly as you might expect. It sorts the array. By default, this is done simply by comparing the elements. However, here you are sorting an array of objects, which are normally unsortable. You therefore have to tell the script how to compare the items, like so:
example.sort(function(a,b) {
return a.id - b.id;
});
Your function will be called for each comparison the engine needs to make (varies based on sorting algorithm used interally, doesn't really matter to us). a and b are the elements currently being compared. The function must return:
a negative number if a is to be considered less than b
a positive number if b is to be considered greater than b
zero if the items are to be considered equal.
In this case, I simplified the expression into a.id - b.id. That's because as per basic maths:
if a.id is less than b.id, the result is negative
if a.id is greater than b.id, the result is positive
if they are equal, the result is zero
Hope this helps!
You would use the library Sugar which could do exactly what you are looking for:
http://sugarjs.com/api
[{age:35,name:'ken'},{age:15,name:'bob'}].groupBy(function(n) {
return n.age;
});
{"15":[{"age":15,"name":"bob"}],"35":[{"age":35,"name":"ken"}]}
But as Niet point out, if the only thing you would do is just a basic sort then the native array sort would be fine, but if it is more SQL like functions you search for, then Sugarjs can be great help.
Else http://www.taffydb.com/ would also be a good choice, that would help you great!

JavaScript: Functional mapping?

Is there a more succinct way to write this?
var me = {};
for (var i in you) {
me[i] = you[i];
}
(where you is an arbitrarily-lengthed JavaScript array)
In other words, given the input of:
var you = [
"what",
"r",
"u"
];
The output, me, becomes:
me = {
0: "what",
1: "r",
2: "u"
};
Like, a one-liner that uses some functional method?
Why do you want to do this? Arrays in JavaScript are Objects, except with some additional properties like slice, splice, push and length.
Internally, Arrays and Objects are stored exactly in the same way: e.g. array[0] is the same as array["0"], or object["0"] (unlike in other languages where adjacent array indices are in fact in adjacent memory - array "indices" are simply converted into strings in JavaScript).
So, if you just want to copy the data, then this will suffice:
me = you.slice(); // me is a copy of you, but is still an array
Or, if you really want some sort of mapping functionality, then underscore.js provides a whole collection of functional programming tools for your perusal.
There is no built-in function that does what you ask, however some of the widely used javascript libraries like jQuery provide such a function. In jQuery's case: jQuery.extend()
Usage:
var me = {};
jQuery.extend(me,someObject);
//or, equivalently -
var me2 = jQuery.extend({},someObject);
jQuery has an extend() function (documentation here). Your code would look like this:
var me = {};
var you = ["what", "r", "u"];
$.extend(me, you);
This would allow you to do things like:
alert("Second element: " + me[1]);
It's a little odd, but I think it's what you're looking for.
I saw what you were trying to achieve with your string formatter. Instead of answering your original question of coming up with a concise implementation of one portion of it, I'll suggest a concise (and more flexible) implementation for the whole thing:
String.prototype.format = function () {
var args = arguments;
return this.replace(/\{(?:(\d+)|(\w+))\}/g, function (s, idx, prop) {
return prop && args[0]
? args[0][prop]
: args[idx];
});
};
When you have a number n inside a token "{n}", it uses the n-th argument for replacement. Otherwise, for non-numerical keys, it picks the corresponding property of the first argument.
For example:
"I have {1} {name}s in my basket.".replace({ type: "fruit", name: "eggplant" }, 4);
Returns:
"I have 4 eggplants in my basket."
The underscore.js library also has a basic extend function.
var me = _({}).extend(you)
or
var me = {}
_(me).extend(you)
or
var me = {}
_.extend(me, you)

Javascript: using tuples as dictionary keys

I have a situation where I want to create a mapping from a tuple to an integer. In python, I would simply use a tuple (a,b) as the key to a dictionary,
Does Javascript have tuples? I found that (a,b) in javascript as an expression just returns b (the last item). Apparently this is inherited from C.
So, as a workaround, I thought I can use arrays instead,
my_map[[a,b]] = c
I tried it at the Firebug console and it seemed to work. Is that a good way to do it?
Another alternative I thought of is to create a string out of the tuples
my_map[""+a+":"+b] = c
So the question is: is there any problem with any of these methods? Is there a better way?
EDIT:
Small clarification: in my case, a,b,c are all integers
EcmaScript doesn't distinguish between indexing a property by name or by [], eg.
a.name
is literally equivalent to
a["name"]
The only difference is that numbers, etc are not valid syntax in a named property access
a.1
a.true
and so on are all invalid syntax.
Alas the reason all of these indexing mechanisms are the same is because in EcmaScript all property names are strings. eg.
a[1]
is effectively interpreted as
a[String(1)]
Which means in your example you do:
my_map[[a,b]] = c
Which becomes
my_map[String([a,b])] = c
Which is essentially the same as what your second example is doing (depending on implementation it may be faster however).
If you want true value-associative lookups you will need to implement it yourself on top of the js language, and you'll lose the nice [] style access :-(
You could use my jshashtable and then use any object as a key, though assuming your tuples are arrays of integers I think your best bet is one you've mentioned yourself: use the join() method of Array to create property names of a regular object. You could wrap this very simply:
function TupleDictionary() {
this.dict = {};
}
TupleDictionary.prototype = {
tupleToString: function(tuple) {
return tuple.join(",");
},
put: function(tuple, val) {
this.dict[ this.tupleToString(tuple) ] = val;
},
get: function(tuple) {
return this.dict[ this.tupleToString(tuple) ];
}
};
var dict = new TupleDictionary();
dict.put( [1,2], "banana" );
alert( dict.get( [1,2] ) );
All object keys in Javascript are strings. Using my_map[[a,b]] = c will produce a key in my_map which is the result of [a,b].toString(): a.toString() + ',' + b.toString(). This may actually be desirable (and is similar to your use of a + ':' + b), but you may run into conflicts if your keys contain the separator (either the comma if you use the array as the key, or the colon if you write the string as you have in your example).
Edit: An alternate approach would be to keep a separate array for key references. Eg:
var keys = [
[a,b],
[c,d]
];
var my_map = {
'keys[0]': /* Whatever [a,b] ought to be the key for */,
'keys[1]': /* Whatever [c,d] ought to be the key for */
};
the most simple and "natural" way to achieve something similar is by using multidimensional arrays, like this:
var my_map = [["blah","blah","bla"],
["foo", "bla", 8],
[324, 2345, 235],
[true, false, "whatever..."]];

Categories

Resources