How do I add syntactic sugar in my Javascript library? - javascript

Right now the library can translate this operation
Select * from List where name = k% order by desc
to
List.filter(function(x) { return x.first_char() == 'k' }).sort().reverse());
Whats the best hack to remove the () so that the developer can write statements like:
List.filter(fn(x) { return x.first_char == 'k' }).sort.reverse;
Naive approach:
maxfn = function() {this[0]..}; Array.prototype.max = maxfn();
But with this approach I can't access 'this'.
I wanted to add a syntactic sugar for
new Array("1","2","3")
to something like :)(suggestions needed)
_("1","2" ,"3")
like we have in scheme where list -> '
I tried to clone the arguments but failed.
Thanks.

For lists you can use JSON notation:
["1", "2", "3"]

You can use JSON notation as suggested by RoBorg, if you control the list... However, there's no cross-browser way to treat a property as a method. Note: spidermonkey (firefox) does support using a getter (get method for a property).

Whats the best hack to remove the ()
Property getters/setters in JavaScript. Unfortunately it's a relatively new JavaScript feature that won't work on IE6/7 (as well as various other older browsers), so it's not really ready for prime-time yet (despite the intro of the linked article).
You could do this particular example by making a JavaScript object that wrapped a String and shadowed all String's methods, then add a static ‘first_char’ property set to the String's first character on initialisation. But it's really not worth it.
new Array("1","2","3")
to something like :)(suggestions needed)
_("1","2" ,"3")
Well that's simple enough:
function _(/* items */) {
var a= new Array();
for (var i= 0; i<arguments.length; i++)
a[i]= arguments[i];
return a;
}
There's no point in doing it nowadays, though, since the array literal syntax:
['1', '2', '3']
has been available since JavaScript 1.1-1.2 era and is available in every browser today. (It predates JSON by many, many years.)

I'll try to answer one by one:
1) Why would you want to remove parenthesis from a functon call?
2) If the "naive" approach is failing it's probably because you are calling the maxFn and assigning the results to Array.prototype.max. It should be like this:
maxfn = function() {this[0]..}; Array.prototype.max = maxfn;
3) RoBorg is correct, just use literal notation to construct arrays on the fly.
Edit:
Here's one way of implementing a max function on an array object. The optional evaluator argument is a function that takes two parameters, the current max value and current value in array. It should return the object that is "greater". Useful for non-primitives.
Array.prototype.max = function(evaluator) {
var max, i = 1; len = this.length;
if (len > 0) max = this[0];
for (; i < len; i++) {
if (evaluator) {
max = evaluator(max, this[i]);
}
else if(max < this[i]) {
max = this[i];
}
}
return max;
};
var a = [1, 3, 4, 5, 6];
alert(a.max());
var b = ["Arnold", "Billy", "Caesar"];
alert(b.max());
var c = ["Arnold", new Date(), 99, true];
alert(c.max());
var d = [1, 3, 4, 5, 6];
alert(d.max(function (max, val) { return max < val ? val : max }));

Related

Allocation-free abstractions in Javascript

I have a general question which is about whether it is possible to make zero-allocation iterators in Javascript. Note that by "iterator" I am not married to the current definition of iterator in ECMAScript, but just a general pattern for iterating over user-defined ranges.
To make the problem concrete, say I have a list like [5, 5, 5, 2, 2, 1, 1, 1, 1] and I want to group adjacent repetitions together, and process it into a form which is more like [5, 3], [2, 2], [1, 4]. I then want to access each of these pairs inside a loop, something like "for each pair in grouped(array), do something with pair". Furthermore, I want to reuse this grouping algorithm in many places, and crucially, in some really hot inner loops (think millions of loops per second).
Question: Is there an iteration pattern to accomplish this which has zero overhead, as if I hand-wrote the loop myself?
Here are the things I've tried so far. Let's suppose for concreteness that I am trying to compute the sum of all pairs. (To be clear I am not looking for alternative ways of writing this code, I am looking for an abstraction pattern: the code is just here to provide a concrete example.)
Inlining the grouping code by hand. This method performs the best, but obscures the intent of the computation. Furthermore, inlining by hand is error-prone and annoying.
function sumPairs(array) {
let sum = 0
for (let i = 0; i != array.length; ) {
let elem = array[i++], count = 1
while (i < array.length && array[i] == elem) { i++; count++; }
// Here we can actually use the pair (elem, count)
sum += elem + count
}
return sum
}
Using a visitor pattern. We can write a reduceGroups function which will call a given visitor(acc, elem, count) for each pair (elem, count), similar to the usual Array.reduce method. With that our computation becomes somewhat clearer to read.
function sumPairsVisitor(array) {
return reduceGroups(array, (sofar, elem, count) => sofar + elem + count, 0)
}
Unfortunately, Firefox in particular still allocates when running this function, unless the closure definition is manually moved outside the function. Furthermore, we lose the ability to use control structures like break unless we complicate the interface a lot.
Writing a custom iterator. We can make a custom "iterator" (not an ES6 iterator) which exposes elem and count properties, an empty property indicating that there are no more pairs remaining, and a next() method which updates elem and count to the next pair. The consuming code looks like this:
function sumPairsIterator(array) {
let sum = 0
for (let iter = new GroupIter(array); !iter.empty; iter.next())
sum += iter.elem + iter.count
return sum
}
I find this code the easiest to read, and it seems to me that it should be the fastest method of abstraction. (In the best possible case, scalar replacement could completely collapse the iterator definition into the function. In the second best case, it should be clear that the iterator does not escape the for loop, so it can be stack-allocated). Unfortunately, both Chrome and Firefox seem to allocate here.
Of the approaches above, the custom-defined iterator performs quite well in most cases, except when you really need to put the pedal to the metal in a hot inner loop, at which point the GC pressure becomes apparent.
I would also be ok with a Javascript post-processor (the Google Closure Compiler perhaps?) which is able to accomplish this.
Check this out. I've not tested its performance but it should be good.
(+) (mostly) compatible to ES6 iterators.
(-) sacrificed ...GroupingIterator.from(arr) in order to not create a (imo. garbage) value-object. That's the mostly in the point above.
afaik, the primary use case for this is a for..of loop anyways.
(+) no objects created (GC)
(+) object pooling for the iterators; (again GC)
(+) compatible with controll-structures like break
class GroupingIterator {
/* object pooling */
static from(array) {
const instance = GroupingIterator._pool || new GroupingIterator();
GroupingIterator._pool = instance._pool;
instance._pool = null;
instance.array = array;
instance.done = false;
return instance;
}
static _pool = null;
_pool = null;
/* state and value / payload */
array = null;
element = null;
index = 0;
count = 0;
/* IteratorResult interface */
value = this;
done = true;
/* Iterator interface */
next() {
const array = this.array;
let index = this.index += this.count;
if (!array || index >= array.length) {
return this.return();
}
const element = this.element = array[index];
while (++index < array.length) {
if (array[index] !== element) break;
}
this.count = index - this.index;
return this;
}
return() {
this.done = true;
// cleanup
this.element = this.array = null;
this.count = this.index = 0;
// return iterator to pool
this._pool = GroupingIterator._pool;
return GroupingIterator._pool = this;
}
/* Iterable interface */
[Symbol.iterator]() {
return this;
}
}
var arr = [5, 5, 5, 2, 2, 1, 1, 1, 1];
for (const item of GroupingIterator.from(arr)) {
console.log("element", item.element, "index", item.index, "count", item.count);
}

A "map" to only part of the elements in Javascript

Is there a way, in Javascript (ES2015), to map only part of a array?
E.g.:
let m = someArray.map(function(n){ if(n===0) return n+1; }
That's it, it would return a mapped array that has less elements than the original one.
Is that something that we could accomplish out of the box with some functional programming technique?
Maps changing the number of elements are icky. It's better to filter first, then map:
let arr2 = arr.filter(e => e === 0).map(e => e + 1);
This is easy with array comprehensions, which were introduced by the ECMAScript 6 draft, but will be postponed to ES7:
let m = [for (n of someArray) if (n===0) n+1]
You won't be able to skip unneeded elements with native Array.prototype.map(). But there's always Array.prototype.reduce() to save the day:
let m = someArray.reduce(function(mem, el) {
if (el === 0) {
mem.push(el + 1);
}
return mem;
}, []);
Array iteration methods are (except for deprecated edge cases1) a concise way of expressing what you'd do with a loop (for(i = 0; i < arr.length; i++) { doSomething(arr[i]) }). When the meaning of the method name is no longer representative, you acheive the exact opposite - instead of using a method that describe the operation, you're using one that doesn't and confuse the reader.
More so, not using this group of methods is more performant. This mostly happens because invoking a function is "expensive".
To use the methods, while being concise, see Bartek's answer.
Alternatively, simply use a loop:
let m = [];
for(v of someArray) {
if(v !== 0) {
m.push(v + 1);
}
}
1 When these methods are called, the length of the array is sampled, and any element added beyond this length from within the callback is not visited.

Using array.splice inside Array prototype

Array.prototype.move = function(oldIndex, newIndex) {
var val = this.splice(oldIndex, 1);
this.splice(newIndex, 0, val[0]);
}
//Testing - Change array position
var testarray = [1, 2, 3, 4];
testarray.move(3, 0);
console.log(testarray);
This produces an error "this.splice is not a function" yet it returns the desired results. Why?
Array.prototype.move = function(oldIndex, newIndex) {
if(Object.prototype.toString.call(this) === '[object Array]') {
if(oldIndex && typeof oldIndex == 'number' && newIndex && typeof newIndex == 'number') {
if(newIndex > this.length) newIndex = this.length;
this.splice(newIndex, 0, this.splice(oldIndex, 1)[0]);
}
}
};
For some reason, the function is being called by the called by the document on load (still haven't quite figured that one out). I added a few checks to verify that this = an array, and then also reset the new index to be equal to the total size if the supplied int was greater than the total length. This solved the error issue I was having, and to me is the simplest way to move objects around in an array. As for why the function is being called onload must be something to do with my code.
You don't need the placeholder variable-
Array.prototype.move = function(oldIndex, newIndex) {
this.splice(newIndex, 0, this.splice(oldIndex, 1)[0]);
}
var a=[1,2,3,4,9,5,6,7,8];
a.move(4,8);
a[8]
/* returned value: (Number)
9
*/
Adding properties to built–in objects is not a good idea if your code must work in arbitrary environments. If you do extend such objects, you shouldn't use property names that are likely to be used by someone else doing the same or similar thing.
There seems to be more than one way to "move" a member, what you seem to be doing can be better named as "swap", so:
if (!Array.prototype.swap) {
Array.prototype.swap = function(a, b) {
var t = this[a];
this[a] = this[b];
this[b] = t;
}
}
I expect that simple re-assignment of values is more efficient than calling methods that need to create new arrays and modify the old one a number of times. But that might be moot anyway. The above is certainly simpler to read and is fewer characters to type.
Note also that the above is stable, array.swap(4,8) gives the same result as array.swap(8,4).
If you want to make a robust function, you first need to work out what to do in cases where either index is greater than array.length, or if one doesn't exist, and so on. e.g.
var a = [,,2]; // a has length 3
a.swap(0,2);
In the above, there are no members at 0 or 1, only at 2. So should the result be:
a = [2]; // a has length 1
or should it be (which will be the result of the above):
a = [2,,undefined]; // a has length 3
or
a = [2,,,]; // a has length 3 (IE may think it's 4, but that's wrong)
Edit
Note that in the OP, the result of:
var b = [,,2];
b.move(0,2);
is
alert(b); // [,2,];
which may not be what is expected, and
b.move(2,0);
alert(b); // [2,,];
so it is not stable either.

How to add the "size" property to Javascript Array?

To add the size() method to the Array object I do:
if (!Array.size) {
Array.prototype.size = function() {
return this.length;
};
}
Is there a simple way to define the size property that will work like length ?
(I don't really need it, I just want to understand if this is something easily achievable in Javascript.)
With ES5 it's possible. Add a size property on the Array prototype,
Object.defineProperty(Array.prototype, "size", {
get: function() {
return this.length;
},
set: function(newLength) {
this.length = newLength;
}
});
var x = [1, 2, 3];
x.length // 3
x.size // 3
x.push(4);
x // [1, 2, 3, 4]
x.length // 4
x.size // 4
x.length = 2;
x // [1, 2]
x.size = 1;
x // [1]
It basically works as a wrapper around length. Appears like a property to the naked eye, but is backed by an underlying function.
Thanks to #Matthew's comment, this size property works like a complete wrapper around length.
I'll assume you already know this is not a real life issue.
length property is modified to suit the size of the array and reflected by methods such as shift() and pop().
So, you would need a method to get the length property.
Anurag shows you how it can be done with ES5.
if(!Array.size)
{
Array.prototype.__defineGetter__('size', function(){
return this.length;
});
}
var a = new Array();
a.push(1);
a.push(4);
console.log(a.size);
Although I'm not entirely sure how cross-browser friendly that is (should work on Chrome and FF at least).

Three map implementations in javascript. Which one is better?

I wrote a simple map implementation for some task. Then, out of curiosity, I wrote two more. I like map1 but the code is kinda hard to read. If somebody is interested, I'd appreciate a simple code review.
Which one is better? Do you know some other way to implement this in javascript?
var map = function(arr, func) {
var newarr = [];
for (var i = 0; i < arr.length; i++) {
newarr[i] = func(arr[i]);
}
return newarr;
};
var map1 = function(arr, func) {
if (arr.length === 0) return [];
return [func(arr[0])].concat(funcmap(arr.slice(1), func));
};
var map2 = function(arr, func) {
var iter = function(result, i) {
if (i === arr.length) return result;
result.push(func(arr[i]));
return iter(result, i+1);
};
return iter([], 0);
};
Thanks!
EDIT
I am thinking about such function in general.
For example, right now I am going to use it to iterate like this:
map(['class1', 'class2', 'class3'], function(cls) {
el.removeClass(cls);
});
or
ids = map(elements, extract_id);
/* elements is a collection of html elements,
extract_id is a func that extracts id from innerHTML */
What about the map implementation used natively on Firefox and SpiderMonkey, I think it's very straight forward:
if (!Array.prototype.map) {
Array.prototype.map = function(fun /*, thisp*/) {
var len = this.length >>> 0; // make sure length is a positive number
if (typeof fun != "function") // make sure the first argument is a function
throw new TypeError();
var res = new Array(len); // initialize the resulting array
var thisp = arguments[1]; // an optional 'context' argument
for (var i = 0; i < len; i++) {
if (i in this)
res[i] = fun.call(thisp, this[i], i, this); // fill the resulting array
}
return res;
};
}
If you don't want to extend the Array.prototype, declare it as a normal function expression.
As a reference, map is implemented as following in jQuery
map: function( elems, callback ) {
var ret = [];
// Go through the array, translating each of the items to their
// new value (or values).
for ( var i = 0, length = elems.length; i < length; i++ ) {
var value = callback( elems[ i ], i );
if ( value != null )
ret[ ret.length ] = value;
}
return ret.concat.apply( [], ret );
}
which seems most similar to your first implementation. I'd say the first one is preferred as it is the simplest to read and understand. But if performance is your concern, profile them.
I think that depends on what you want map to do when func might change the array. I would tend to err on the side of simplicity and sample length once.
You can always specify the output size as in
var map = function(arr, func) {
var n = arr.length & 0x7fffffff; // Make sure n is a non-neg integer
var newarr = new Array(n); // Preallocate array size
var USELESS = {};
for (var i = 0; i < n; ++i) {
newarr[i] = func.call(USELESS, arr[i]);
}
return newarr;
};
I used the func.call() form instead of just func(...) instead since I dislike calling user supplied code without specifying what 'this' is, but YMMV.
This first one is most appropriate. Recursing one level for every array item may make sense in a functional language, but in a procedural language without tail-call optimisation it's insane.
However, there is already a map function on Array: it is defined by ECMA-262 Fifth Edition and, as a built-in function, is going to be the optimal choice. Use that:
alert([1,2,3].map(function(n) { return n+3; })); // 4,5,6
The only problem is that Fifth Edition isn't supported by all current browsers: in particular, the Array extensions are not present in IE. But you can fix that with a little remedial work on the Array prototype:
if (!Array.prototype.map) {
Array.prototype.map= function(fn, that) {
var result= new Array(this.length);
for (var i= 0; i<this.length; i++)
if (i in this)
result[i]= fn.call(that, this[i], i, this);
return result;
};
}
This version, as per the ECMA standard, allows an optional object to be passed in to bind to this in the function call, and skips over any missing values (it's legal in JavaScript to have a list of length 3 where there is no second item).
There's something wrong in second method. 'funcmap' shouldn't be changed to 'map1'?
If so - this method loses, as concat() method is expensive - creates new array from given ones, so has to allocate extra memory and execute in O(array1.length + array2.length).
I like your first implementation best - it's definitely easiest to understand and seems quick in execution to me. No extra declaration (like in third way), extra function calls - just one for loop and array.length assignments.
I'd say the first one wins on simplicity (and immediate understandability); performance will be highly dependent on what the engine at hand optimizes, so you'd have to profile in the engines you want to support.

Categories

Resources