Creating the underscore reduce function from scratch - javascript

I am working on creating my own callback functions and higher order function groups. I am stuck on replicating the underscore reduce function or ._reduce function. Can someone help me understand how it works underneath the hood it has been a few days for me and I am stumped. Here is what I have so far. Please understand I am not utilizing the underscore library, I am trying to replicate it so that I can further my understanding on higher order functions. Thanks.
var reduce = function(collection, iterator, accumulator) {
var iterator = function(startPoint, combiner){
for(var i = 0; i <combiner.length; i++){
startPoint += combiner[i];
}
return iterator(accumulator, collection);
}

A simple recursive function does the trick
// arr - some array of values
// f - the reducing function
// acc - initial value for the accumulator
function reduce(arr, f, acc) {
if (arr.length === 0)
return acc
else
return reduce(arr.slice(1), f, f(acc, arr[0]))
}
// --------------------------------------------------
// example 1:
// reduce an array of numbers using an adding function
var ex1 = reduce([1,2,3], function(acc, x) { return acc + x }, 0)
console.log(ex1)
//=> 6
// --------------------------------------------------
// example 2:
// reduce an array of pairs to a mapping object
var ex2 = reduce([['a', 1], ['b', 2], ['c', 3]], function(acc, pair) {
var key = pair[0]
var value = pair[1]
acc[key] = value
return acc
}, {})
console.log(ex2)
//=> { a: 1, b: 2, c: 3 }
As #torazaburo points out in a comment, if you can use ES6, destructuring assignment cleans up the implementation even more
// ES6
function reduce([x, ...xs], f, acc) {
if (x === undefined)
return acc
else
return reduce(xs, f, f(acc, x))
}
Or it gets super sugary sweet with arrow functions
// ES6, same as above but using arrow function and ternary expression
const reduce = ([x, ...xs], f, acc)=>
x === undefined ? acc : reduce(xs, f, f(acc, x))
The Underscore implementation does provide some other conveniences though I'm guessing these are here to maintain compatibility with native Array.prototype.reduce. I personally wouldn't implement reduce this way, but that's beside the point.
Underscore passes an iterator and arr reference to the callback function.
Underscore allows you to change the context for the callback function
Here's a revised implementation which supports these other features
// our reduce version 2.0
function reduce(collection, iterator, memo, context) {
function loop(memo, i) {
if (collection.length === i)
return memo
else
return loop(iterator.call(context, memo, collection[i], i, collection), i + 1)
}
return loop(memo, 0)
}
You can use it the same as above only now it provides more information to the callback
NOTE
I've purposefully decided not to implement a behaviour of Underscore's reduce that allows you to perform a reduction without an initial value. Supporting this behaviour results in unsafe code and should never have made it into Underscore in the first place.

It's something like it:
function reduce(array, combine, start) {
for (var i = 0; i < array.length; i++)
start = combine(start, array[i]);
return start;
}
console.log(reduce([1, 2, 3, 4], function(a, b) {
return a + b;
}, 0));
Link Reference: http://eloquentjavascript.net/05_higher_order.html

In the comments of these answers, there has been a lot of confusion between Underscore's reduce and Array.prototype.reduce. Two notes:
Underscore's reduce allows for an empty collection and no seed value. In this case, it will not throw an error but rather returns undefined. naomik has convinced me that this is not safe. For example _([]).reduce(function(a, b) { return a + b}); should either throw an error or return an empty list.
Underscore's reduce works on both objects and arrays.
Now, to my original post:
I actually did the same thing—implement the key functions from Underscore from scratch—a while back, and reduce was probably the trickiest. I think reduce is easier to grok with a non-functional reduce (credit to naomik for this):
function reduce(arr, func, seed) {
var result = seed,
len = arr.length,
i = 0;
for (; i < len; i++) {
result = func(result, arr[i])
}
return result
}
Underscore's implementation is a bit more complex, handling objects and arrays, empty collections, and an optional seed value. It also uses each instead of a for loop, since that is more functional in style. This is my implementation of Underscore's reduce:
var reduce = function(coll, func, seed) {
// `isEmpty` (not shown) handles empty arrays, strings, and objects.
// Underscore accepts an optional seed value and does not
// throw an error if given an empty collection and no seed.
if (isEmpty(coll)) {
return coll;
}
var noSeed = arguments.length < 3;
// `each` (not shown) should treat arrays and objects
// in the same way.
each(coll, function(item, i) {
if (noSeed) {
// This condition passes at most once. If it passes,
// this means the user did not provide a seed value.
// Default to the first item in the list.
noSeed = false;
seed = item;
} else {
seed = func(seed, item, i);
}
});
return seed;
};

Related

2 Sum algorithm explantion?

I am a noobie in JavaScript algorithm and cannot understand this optimal solution of the 2-sum
function twoNumberSum(array, target) {
const nums = {};
for (const num of array) {
const potentialMatch = target - num;
console.log('potential', potentialMatch);
if (potentialMatch in nums) {
return [potentialMatch, num]
} else {
nums[num] = true;
}
}
}
So the 2-sum problem basically says "find two numbers in an array that sum to the given target, and return their index". Let's walk through this code and talk about what's happening.
First, we start the function; I'm going to assume this makes sense (a function that's called twoNumberSum that takes in two arguments; namely, array and target) - note that in JS, we don't annotate types, so there is no return type
Now, first thing we do is create a new object called nums. In JS, objects are effectively hash maps (with some very important differences - see my note below); they store a key and a corresponding value. In JS, a key can be any string or number
Next, we start our iteration. If we do for (const a of b), and b is an array, this iterates over all the values of the array, with each iteration having that value stored in a.
Next, we subtract our current value from the target. Then comes the key line: if (potentialMatch in nums). The in keyword checks for the existence of a key: 'hello' in obj returns true if obj has the key 'hello'.
In this case, if we find this potential match, then that means we have found another number that is equal to target - num, which of course means we've found the other partner for our sum! So in this case, we simply return the two numbers. If, on the other hand, we do not find this potentialMatch, that means we need to keep looking. But we do want to remember we've seen this number - thus, we add it as a key by doing nums[num] = true (this creates a new key-value pair; namely the key is num and the value is true).
As one of the comments explained, this is just trying to keep track of a list of numbers; however, the author is trying to be clever by using a Hash Table instead of a normal array. This way, lookups are O(1) instead of O(n). For eyes not used to JS semantics, another way of explaining this code is that we build up a Map of the numbers, and then we check that map for our target value.
I mentioned earlier that using objects as hash tables isn't the best idea; this is because if you aren't careful, if you use user-provided keys, you can accidentally mess with what's called the Prototype Chain. This is beyond this discussion, but a better way forward would be to use a Set:
function twoNumberSum(array, target) {
// Create a new Hash Set. Sets take in an iterable, so we could
// Do it this way. But to remain as close to your original solution
// as possible, we won't for now, and instead populate it as we go
// const nums = new Set(array);
const nums = new Set();
for (const num of array) {
const potentialMatch = target - num;
if (nums.has(potentialMatch)) {
return [potentialMatch, num];
} else {
nums.add(num);
}
}
Sometimes, the problem instead asks for you to return the indices; using a Map instead makes this relatively trivial. Just store the index as the value and you're good to go!
function twoNumberSum(array, target) {
// Create the new map instead
const nums = new Map();
for (let n = 0; n < array.length; ++n) {
const potentialMatch = target - array[n];
if (nums.has(potentialMatch)) {
return [nums.get(potentialMatch), n];
} else {
nums.set(array[n], n);
}
}
Let me explain to you what it's all is working-.
function twoNumberSum(array, target) {
// This is and object in Javascript
const nums = {};
for (const num of array) { // This is for of loop which iterates the array.
//For of Doc - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of
// Here's its calculating the potential.
const potentialMatch = target - num;
console.log('potential - ' + potentialMatch);
/**
* Nowhere `in` is used which check if any property exists in an object or not.
* in Usage - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/in
*
* It checks whether potential exists in the `nums` object, If exist it returns the array
* with potentialMatch and num to which it is matched.
*
* If the number is not there in nums object. It's setting there in else block
* to match in net iteration.
*/
if (potentialMatch in nums) {
return [potentialMatch, num]
} else {
nums[num] = true;
/**
* It forms an object when the potential match doesn't exist in nums for checking in the next iteration
* {
* 1: true,
* 2: true
* }
*/
}
console.log(nums)
}
}
console.log(twoNumberSum([1, 2, 4, 5, 6, 7, 8], 3))
You can also Run it from JSBin

Check if a word matches another in a map function in JS [duplicate]

I would like to filter an array of items by using the map() function. Here is a code snippet:
var filteredItems = items.map(function(item)
{
if( ...some condition... )
{
return item;
}
});
The problem is that filtered out items still uses space in the array and I would like to completely wipe them out.
Any idea?
EDIT: Thanks, I forgot about filter(), what I wanted is actually a filter() then a map().
EDIT2: Thanks for pointing that map() and filter() are not implemented in all browsers, although my specific code was not intended to run in a browser.
You should use the filter method rather than map unless you want to mutate the items in the array, in addition to filtering.
eg.
var filteredItems = items.filter(function(item)
{
return ...some condition...;
});
[Edit: Of course you could always do sourceArray.filter(...).map(...) to both filter and mutate]
Inspired by writing this answer, I ended up later expanding and writing a blog post going over this in careful detail. I recommend checking that out if you want to develop a deeper understanding of how to think about this problem--I try to explain it piece by piece, and also give a JSperf comparison at the end, going over speed considerations.
That said, **The tl;dr is this:
To accomplish what you're asking for (filtering and mapping within one function call), you would use Array.reduce()**.
However, the more readable and (less importantly) usually significantly faster2 approach is to just use filter and map chained together:
[1,2,3].filter(num => num > 2).map(num => num * 2)
What follows is a description of how Array.reduce() works, and how it can be used to accomplish filter and map in one iteration. Again, if this is too condensed, I highly recommend seeing the blog post linked above, which is a much more friendly intro with clear examples and progression.
You give reduce an argument that is a (usually anonymous) function.
That anonymous function takes two parameters--one (like the anonymous functions passed in to map/filter/forEach) is the iteratee to be operated on. There is another argument for the anonymous function passed to reduce, however, that those functions do not accept, and that is the value that will be passed along between function calls, often referred to as the memo.
Note that while Array.filter() takes only one argument (a function), Array.reduce() also takes an important (though optional) second argument: an initial value for 'memo' that will be passed into that anonymous function as its first argument, and subsequently can be mutated and passed along between function calls. (If it is not supplied, then 'memo' in the first anonymous function call will by default be the first iteratee, and the 'iteratee' argument will actually be the second value in the array)
In our case, we'll pass in an empty array to start, and then choose whether to inject our iteratee into our array or not based on our function--this is the filtering process.
Finally, we'll return our 'array in progress' on each anonymous function call, and reduce will take that return value and pass it as an argument (called memo) to its next function call.
This allows filter and map to happen in one iteration, cutting down our number of required iterations in half--just doing twice as much work each iteration, though, so nothing is really saved other than function calls, which are not so expensive in javascript.
For a more complete explanation, refer to MDN docs (or to my post referenced at the beginning of this answer).
Basic example of a Reduce call:
let array = [1,2,3];
const initialMemo = [];
array = array.reduce((memo, iteratee) => {
// if condition is our filter
if (iteratee > 1) {
// what happens inside the filter is the map
memo.push(iteratee * 2);
}
// this return value will be passed in as the 'memo' argument
// to the next call of this function, and this function will have
// every element passed into it at some point.
return memo;
}, initialMemo)
console.log(array) // [4,6], equivalent to [(2 * 2), (3 * 2)]
more succinct version:
[1,2,3].reduce((memo, value) => value > 1 ? memo.concat(value * 2) : memo, [])
Notice that the first iteratee was not greater than one, and so was filtered. Also note the initialMemo, named just to make its existence clear and draw attention to it. Once again, it is passed in as 'memo' to the first anonymous function call, and then the returned value of the anonymous function is passed in as the 'memo' argument to the next function.
Another example of the classic use case for memo would be returning the smallest or largest number in an array. Example:
[7,4,1,99,57,2,1,100].reduce((memo, val) => memo > val ? memo : val)
// ^this would return the largest number in the list.
An example of how to write your own reduce function (this often helps understanding functions like these, I find):
test_arr = [];
// we accept an anonymous function, and an optional 'initial memo' value.
test_arr.my_reducer = function(reduceFunc, initialMemo) {
// if we did not pass in a second argument, then our first memo value
// will be whatever is in index zero. (Otherwise, it will
// be that second argument.)
const initialMemoIsIndexZero = arguments.length < 2;
// here we use that logic to set the memo value accordingly.
let memo = initialMemoIsIndexZero ? this[0] : initialMemo;
// here we use that same boolean to decide whether the first
// value we pass in as iteratee is either the first or second
// element
const initialIteratee = initialMemoIsIndexZero ? 1 : 0;
for (var i = initialIteratee; i < this.length; i++) {
// memo is either the argument passed in above, or the
// first item in the list. initialIteratee is either the
// first item in the list, or the second item in the list.
memo = reduceFunc(memo, this[i]);
// or, more technically complete, give access to base array
// and index to the reducer as well:
// memo = reduceFunc(memo, this[i], i, this);
}
// after we've compressed the array into a single value,
// we return it.
return memo;
}
The real implementation allows access to things like the index, for example, but I hope this helps you get an uncomplicated feel for the gist of it.
That's not what map does. You really want Array.filter. Or if you really want to remove the elements from the original list, you're going to need to do it imperatively with a for loop.
Array Filter method
var arr = [1, 2, 3]
// ES5 syntax
arr = arr.filter(function(item){ return item != 3 })
// ES2015 syntax
arr = arr.filter(item => item != 3)
console.log( arr )
You must note however that the Array.filter is not supported in all browser so, you must to prototyped:
//This prototype is provided by the Mozilla foundation and
//is distributed under the MIT license.
//http://www.ibiblio.org/pub/Linux/LICENSES/mit.license
if (!Array.prototype.filter)
{
Array.prototype.filter = function(fun /*, thisp*/)
{
var len = this.length;
if (typeof fun != "function")
throw new TypeError();
var res = new Array();
var thisp = arguments[1];
for (var i = 0; i < len; i++)
{
if (i in this)
{
var val = this[i]; // in case fun mutates this
if (fun.call(thisp, val, i, this))
res.push(val);
}
}
return res;
};
}
And doing so, you can prototype any method you may need.
TLDR: Use map (returning undefined when needed) and then filter.
First, I believe that a map + filter function is useful since you don't want to repeat a computation in both. Swift originally called this function flatMap but then renamed it to compactMap.
For example, if we don't have a compactMap function, we might end up with computation defined twice:
let array = [1, 2, 3, 4, 5, 6, 7, 8];
let mapped = array
.filter(x => {
let computation = x / 2 + 1;
let isIncluded = computation % 2 === 0;
return isIncluded;
})
.map(x => {
let computation = x / 2 + 1;
return `${x} is included because ${computation} is even`
})
// Output: [2 is included because 2 is even, 6 is included because 4 is even]
Thus compactMap would be useful to reduce duplicate code.
A really simple way to do something similar to compactMap is to:
Map on real values or undefined.
Filter out all the undefined values.
This of course relies on you never needing to return undefined values as part of your original map function.
Example:
let array = [1, 2, 3, 4, 5, 6, 7, 8];
let mapped = array
.map(x => {
let computation = x / 2 + 1;
let isIncluded = computation % 2 === 0;
if (isIncluded) {
return `${x} is included because ${computation} is even`
} else {
return undefined
}
})
.filter(x => typeof x !== "undefined")
I just wrote array intersection that correctly handles also duplicates
https://gist.github.com/gkucmierz/8ee04544fa842411f7553ef66ac2fcf0
// array intersection that correctly handles also duplicates
const intersection = (a1, a2) => {
const cnt = new Map();
a2.map(el => cnt[el] = el in cnt ? cnt[el] + 1 : 1);
return a1.filter(el => el in cnt && 0 < cnt[el]--);
};
const l = console.log;
l(intersection('1234'.split``, '3456'.split``)); // [ '3', '4' ]
l(intersection('12344'.split``, '3456'.split``)); // [ '3', '4' ]
l(intersection('1234'.split``, '33456'.split``)); // [ '3', '4' ]
l(intersection('12334'.split``, '33456'.split``)); // [ '3', '3', '4' ]
First you can use map and with chaining you can use filter
state.map(item => {
if(item.id === action.item.id){
return {
id : action.item.id,
name : item.name,
price: item.price,
quantity : item.quantity-1
}
}else{
return item;
}
}).filter(item => {
if(item.quantity <= 0){
return false;
}else{
return true;
}
});
following statement cleans object using map function.
var arraytoclean = [{v:65, toberemoved:"gronf"}, {v:12, toberemoved:null}, {v:4}];
arraytoclean.map((x,i)=>x.toberemoved=undefined);
console.dir(arraytoclean);

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);
}

Javascript, looping through an array and the arguments "object"

I can't get my head around this, i'm using filter to loop through an array and filter out all the integers passed as arguments ,i'm not limited in the number of arguments.
But i'm stuck here when it's about to get back to the function the value of the arguments object, at least more that once.
In my code below, obviously it's not fully working because i'm doing a return within the for…in loop, this is where i don't get how I can get the this second loop without having i re-initialised to 0…
function destroyer(arr) {
var args = arguments.length;
var arg0 = arguments[0];
var Nargs = Array.prototype.slice.call(arguments, 1);
var newArr = [];
newArr = arg0.filter(filtre);
/*Only the first argument is
filtered out,here it's the value "2",
but should filter out [2,3].
The expected result is [1,1]*/
console.log(newArr);
return newArr;
function filtre(e){
for (var i in Nargs){
return e !== Nargs[i];
}
}
}
destroyer([1, 2, 3, 1, 2, 3], 2,3);
Hope this is clear enough,
Thanks for any input !
Matth.
While rgthree provided a solution very similar to what you already had, I wanted to provide a solution that takes advantage of newer ES6 features for modern browsers. If you run this through Babel, it will result in essentially the same code as in that solution, though it will still require a shim for the includes call.
function destroyer(source, ...args) {
return source.filter(el => !args.includes(el));
}
Explanation:
...args uses the spread operator to get all but the first argument into an array named args.
We can directly return the filtered array, assuming you don't need to log it and that was just for debugging.
el => !args.includes(el) is an anonymous arrow function. Since no braces are used, it will automatically return the result of the expression.
Since args is an array, we can directly use Array.prototype.includes to check if the current element is in the arguments to be removed. If it exists, we want to remove it and thus invert the return with !. An alternative could be the following: el => args.includes(el) == false.
Since your return statement in your filtre function is always executed on the first run, it's only returning the whether the first number in Nargs is equal to the current item. Instead, you can use indexOf for full browser support (which returns the index of the item in an array, or "-1" if it's not in an array) instead of a loop (or includes, or a loop, etc. as shown further below):
function destroyer(arr) {
var args = arguments.length;
var arg0 = arguments[0];
var Nargs = Array.prototype.slice.call(arguments, 1);
var newArr = [];
function filtre(e){
return Nargs.indexOf(e) === -1;
}
newArr = arg0.filter(filtre);
console.log(newArr);
return newArr;
}
destroyer([1, 2, 3, 1, 2, 3], 2,3);
If you don't need Internet Explorer support, you could use Array.includes:
function filtre(e){
return !Nargs.includes(e);
}
And finally, if you really wanted to use a loop, you would only want to filter an item (return false) once it's found otherwise allow it (return true):
function filtre(e){
for (var i = 0, l = Nargs.length; i < l; i++) {
if (e === Nargs[i])
return false;
}
return true;
}

How can I convert the "arguments" object to an array in JavaScript?

Want to improve this post? Provide detailed answers to this question, including citations and an explanation of why your answer is correct. Answers without enough detail may be edited or deleted.
The arguments object in JavaScript is an odd wart—it acts just like an array in most situations, but it's not actually an array object. Since it's really something else entirely, it doesn't have the useful functions from Array.prototype like forEach, sort, filter, and map.
It's trivially easy to construct a new array from an arguments object with a simple for loop. For example, this function sorts its arguments:
function sortArgs() {
var args = [];
for (var i = 0; i < arguments.length; i++)
args[i] = arguments[i];
return args.sort();
}
However, this is a rather pitiful thing to have to do simply to get access to the extremely useful JavaScript array functions. Is there a built-in way to do it using the standard library?
ES6 using rest parameters
If you are able to use ES6 you can use:
Rest Parameters
function sortArgs(...args) {
return args.sort(function (a, b) { return a - b; });
}
document.body.innerHTML = sortArgs(12, 4, 6, 8).toString();
As you can read in the link
The rest parameter syntax allows us to represent an indefinite number of arguments as an array.
If you are curious about the ... syntax, it is called Spread Operator and you can read more here.
ES6 using Array.from()
Using Array.from:
function sortArgs() {
return Array.from(arguments).sort(function (a, b) { return a - b; });
}
document.body.innerHTML = sortArgs(12, 4, 6, 8).toString();
Array.from simply convert Array-like or Iterable objects into Array instances.
ES5
You can actually just use Array's slice function on an arguments object, and it will convert it into a standard JavaScript array. You'll just have to reference it manually through Array's prototype:
function sortArgs() {
var args = Array.prototype.slice.call(arguments);
return args.sort();
}
Why does this work? Well, here's an excerpt from the ECMAScript 5 documentation itself:
NOTE: The slice function is intentionally generic; it does not require that its this value be an Array object. Therefore it can be transferred to other kinds of objects for use as a method. Whether the slice function can be applied successfully to a host object is implementation-dependent.
Therefore, slice works on anything that has a length property, which arguments conveniently does.
If Array.prototype.slice is too much of a mouthful for you, you can abbreviate it slightly by using array literals:
var args = [].slice.call(arguments);
However, I tend to feel that the former version is more explicit, so I'd prefer it instead. Abusing the array literal notation feels hacky and looks strange.
It's also worth referencing this Bluebird promises library wiki page that shows how to manage the arguments object into array in a way that makes the function optimizable under V8 JavaScript engine:
function doesntLeakArguments() {
var args = new Array(arguments.length);
for(var i = 0; i < args.length; ++i) {
args[i] = arguments[i];
}
return args;
}
This method is used in favor of var args = [].slice.call(arguments);. The author also shows how a build step can help reduce the verbosity.
function sortArgs(){ return [].slice.call(arguments).sort() }
// Returns the arguments object itself
function sortArgs(){ return [].sort.call(arguments) }
Some array methods are intentionally made not to require the target object to be an actual array. They only require the target to have a property named length and indices (which must be zero or larger integers).
[].sort.call({0:1, 1:0, length:2}) // => ({0:0, 1:1, length:2})
Use:
function sortArguments() {
return arguments.length === 1 ? [arguments[0]] :
Array.apply(null, arguments).sort();
}
Array(arg1, arg2, ...) returns [arg1, arg2, ...]
Array(str1) returns [str1]
Array(num1) returns an array that has num1 elements
You must check number of arguments!
Array.slice version (slower):
function sortArguments() {
return Array.prototype.slice.call(arguments).sort();
}
Array.push version (slower, faster than slice):
function sortArguments() {
var args = [];
Array.prototype.push.apply(args, arguments);
return args.sort();
}
Move version (slower, but small size is faster):
function sortArguments() {
var args = [];
for (var i = 0; i < arguments.length; ++i)
args[i] = arguments[i];
return args.sort();
}
Array.concat version (slowest):
function sortArguments() {
return Array.prototype.concat.apply([], arguments).sort();
}
If you're using jQuery, the following is a good deal easier to remember in my opinion:
function sortArgs(){
return $.makeArray(arguments).sort();
}
In ECMAScript 6 there's no need to use ugly hacks like Array.prototype.slice(). You can instead use spread syntax (...).
(function() {
console.log([...arguments]);
}(1, 2, 3))
It may look strange, but it's fairly simple. It just extracts arguments' elements and put them back into the array. If you still don't understand, see this examples:
console.log([1, ...[2, 3], 4]);
console.log([...[1, 2, 3]]);
console.log([...[...[...[1]]]]);
Note that it doesn't work in some older browsers like IE 11, so if you want to support these browsers, you should use Babel.
Here is benchmark of several methods converting arguments into array.
As for me, the best solution for small amount of arguments is:
function sortArgs (){
var q = [];
for (var k = 0, l = arguments.length; k < l; k++){
q[k] = arguments[k];
}
return q.sort();
}
For other cases:
function sortArgs (){ return Array.apply(null, arguments).sort(); }
Here's a clean and concise solution:
function argsToArray() {
return Object.values(arguments);
}
// example usage
console.log(
argsToArray(1, 2, 3, 4, 5)
.map(arg => arg*11)
);
Object.values( ) will return the values of an object as an array, and since arguments is an object, it will essentially convert arguments into an array, thus providing you with all of an array's helper functions such as map, forEach, filter, etc.
I recommend using ECMAScript 6 spread operator, which will Bind trailing parameters to an array. With this solution you don't need to touch the arguments object and your code will be simplified. The downside of this solution is that it does not work across most browsers, so instead you will have to use a JS compiler such as Babel. Under the hood Babel transforms arguments into a Array with a for loop.
function sortArgs(...args) {
return args.sort();
}
If you can not use a ECMAScript 6, I recommend looking at some of the other answers such as #Jonathan Fingland
function sortArgs() {
var args = Array.prototype.slice.call(arguments);
return args.sort();
}
Lodash:
var args = _.toArray(arguments);
in action:
(function(){ console.log(_.toArray(arguments).splice(1)); })(1, 2, 3)
produces:
[2,3]
Use Array.from(), which takes an array-like object (such as arguments) as argument and converts it to array:
(function() {
console.log(Array.from(arguments));
}(1, 2, 3));
Note that it doesn't work in some older browsers like IE 11, so if you want to support these browsers, you should use Babel.
function sortArg(){
var args = Array.from(arguments); return args.sort();
}
function sortArg(){
var args = Array.from(arguments);
return args.sort();
}
console.log(sortArg('a', 'b', 1, 2, '34', 88, 20, '19', 39, 'd', 'z', 'ak', 'bu', 90));
Another Answer.
Use Black Magic Spells:
function sortArguments() {
arguments.__proto__ = Array.prototype;
return arguments.slice().sort();
}
Firefox, Chrome, Node.js, IE11 are OK.
Try using Object.setPrototypeOf()
Explanation: Set prototype of arguments to Array.prototype
function toArray() {
return Object.setPrototypeOf(arguments, Array.prototype)
}
console.log(toArray("abc", 123, {def:456}, [0,[7,[14]]]))
Explanation: Take each index of arguments , place item into an array at corresponding index of array.
could alternatively use Array.prototype.map()
function toArray() {
return [].map.call(arguments, (_,k,a) => a[k])
}
console.log(toArray("abc", 123, {def:456}, [0,[7,[14]]]))
Explanation: Take each index of arguments , place item into an array at corresponding index of array.
for..of loop
function toArray() {
let arr = []; for (let prop of arguments) arr.push(prop); return arr
}
console.log(toArray("abc", 123, {def:456}, [0,[7,[14]]]))
or Object.create()
Explanation: Create object, set properties of object to items at each index of arguments; set prototype of created object to Array.prototype
function toArray() {
var obj = {};
for (var prop in arguments) {
obj[prop] = {
value: arguments[prop],
writable: true,
enumerable: true,
configurable: true
}
}
return Object.create(Array.prototype, obj);
}
console.log(toArray("abc", 123, {def: 456}, [0, [7, [14]]]))
Benshmarck 3 methods :
function test()
{
console.log(arguments.length + ' Argument(s)');
var i = 0;
var loop = 1000000;
var t = Date.now();
while(i < loop)
{
Array.prototype.slice.call(arguments, 0);
i++;
}
console.log(Date.now() - t);
i = 0,
t = Date.now();
while(i < loop)
{
Array.apply(null, arguments);
i++;
}
console.log(Date.now() - t);
i = 0,
t = Date.now();
while(i < loop)
{
arguments.length == 1 ? [arguments[0]] : Array.apply(null, arguments);
i++;
}
console.log(Date.now() - t);
}
test();
test(42);
test(42, 44);
test(42, 44, 88, 64, 10, 64, 700, 15615156, 4654, 9);
test(42, 'truc', 44, '47', 454, 88, 64, '#ehuehe', 10, 64, 700, 15615156, 4654, 9,97,4,94,56,8,456,156,1,456,867,5,152489,74,5,48479,89,897,894,894,8989,489,489,4,489,488989,498498);
RESULT?
0 Argument(s)
256
329
332
1 Argument(s)
307
418
4
2 Argument(s)
375
364
367
10 Argument(s)
962
601
604
40 Argument(s)
3095
1264
1260
Enjoy !
function sortArgs(...args) {
return args.sort(function (a, b) { return a - b; });
}
document.body.innerHTML = sortArgs(1, 2, 3, 4).toString();
Although rest parameters work well, if you want to continue to use arguments for some reason, consider
function sortArgs() {
return [...arguments].sort()
}
[...arguments] can be considered a sort of alternative to Array.from(arguments), which also works perfectly well.
An ES7 alternative is an array comprehension:
[for (i of arguments) i].sort()
This could be easiest if you want to process or filter the arguments prior to sorting:
[for (i of arguments) if (i % 2) Math.log(i)].sort()
function x(){
var rest = [...arguments]; console.log(rest);return
rest.constructor;
};
x(1,2,3)
I tried simple destructing technique
The Arguments object is only available inside a function body. Although you can index the Arguments Object like an array, it is not an array. It does not have any array properties other than length.
// function arguments length 5
function properties(a,b,c,d,e){
var function_name= arguments.callee.name
var arguments_length= arguments.length;
var properties_length= properties.length;
var function_from= properties.caller.name;
console.log('I am the function name: '+ function_name);
console.log('I am the function length, I am function spacific: '+ properties_length);
console.log('I am the arguments length, I am context/excution spacific: '+ arguments_length);
console.log('I am being called From: '+ function_from );
}
// arguments 3
function parent(){
properties(1,2,3);
}
//arguments length 3 because execution spacific
parent();
Although it can be indexed like an array as you can see in this example:
function add(){
var sum=0;
for(var i=0; i< arguments.length;i++){
sum = sum + arguments[i];
}
return sum;
}
console.log(add(1,2,3));
However, the Arguments object is not an array and does not have any other properties other than length.
You can convert the arguments object into an array at which point you can access the Arguments object.
There are many ways you can access the arguments object inside a function body, and these include:
you can call the Array.prototoype.slice.call method.
Array.prototype.slice.call(arguments)
function giveMeArgs(arg1,arg2){
var args = Array.prototype.slice.call(arguments);
return args
}
console.log( giveMeArgs(1,2));
you can use the array literal
[].slice.call(arguments).
function giveMeArgs(arg1,arg2){
var args = [].slice.call(arguments);
return args;
}
console.log( giveMeArgs(1,2) );
you can use Rest ...
function giveMeArgs(...args){
return args;
}
console.log(giveMeArgs(1,2))
you can use spread [...]
function giveMeArgs(){
var args = [...arguments];
return args;
}
console.log(giveMeArgs(1,2));
you can use Array.from()
function giveMeArgs(){
var args = Array.from(arguments);
return args;
}
console.log(giveMeArgs(1,2));
You can create a reusable function to do it with any arguments, the simplest one is something like this:
function sortArgs() {
return [...arguments].sort();
}
sortArgs('ali', 'reza', 1, 2, 'a'); //[1, 2, "a", "ali", "reza"];
Spread syntax can be used in ES6 and above...
But if you'd like to use something compatible with ES5 and below, you can use Array.prototype.slice.call, so you code looks like this:
function sortArgs() {
return Array.prototype.slice.call(arguments).sort();
}
sortArgs('ali', 'reza', 1, 2, 'a'); //[1, 2, "a", "ali", "reza"];
There are also few other ways to do this, for Example using Array.from or loop through the arguments and assign them to a new array...
This is a very old question, but I think I have a solution that is slightly easier to type than previous solutions and doesn't rely on external libraries:
function sortArguments() {
return Array.apply(null, arguments).sort();
}

Categories

Resources