Why doesn't this iterate?
(Array(5)).reduce(function(cur,prv,n,a){alert(n)},'');
I never reached the function body, seemingly ignoring all of the empty values, which is not what I want it to do.
As far as I understand Array(5) returns an instance of Array Object with a property length (value 5), but without values. One way to be able to use reduce (or other array methods like map) here is:
String(Array(5)).split(',').reduce(function (cur, prv, n, a) { /* ... */ })
An alternative could be:
Array.apply(null,{length: 5}).map( [callback] );
// or
Array.apply(null,Array(5)).map( [callback] );
But that will encounter a maximum call stack error at some point. The string/split method keeps working (albeit a bit slower) for large arrays. Using node.js on my (not so fast) computer mapping Array(1000000) with the string/split-method lasts 0.47 seconds, the array.apply method for the same crashes with a RangeError (Maximum call stack size exceeded).
You can also write a 'static' Array method to create an Array with n values and all usable/applicable Array methods in place:
Array.create = function (n, mapCB, method, initial) {
method = method in [] ? method : 'map';
var nwArr = ( function(nn){ while (nn--) this.push(undefined); return this; } )
.call([], n);
return mapCB ? nwArr[method](mapCB, initial) : nwArr;
};
// usages
Array.create(5, [callback], 'reduce', '');
Array.create(5).reduce( [callback], '');
Array.create(5).map( [callback] ).reduce( [callback] );
// etc.
As others have stated, the implementation of reduce causes this to throw a TypeError because the calling array has no initial values. To initialize an array with initial values of undefined and successfully invoke the function, try something like this:
Array.apply(undefined, Array(5)).reduce(function(cur,prv,n,a){alert(n)},'');
Related
I'm having trouble using a function within a Node.js module. I'm using a custom sort function to sort an array of objects by the value of a certain property.
exports.getResult = function(cards) {
cards.sort(sortByField('suit'));
...
...
...
return cards;
}
function sortByField(fieldName) {
return function(card1, card2) {
return card1[fieldName] < card2[fieldName]
};
}
When I use the getResult function NOT as an export, and call it from the same file, everything works as expected. The card object with the highest suit value is first and the card object with the lowest value is last.
However, when I call the function as an export from my index.js, the sort doesn't happen. There is no error, the rest of the function just executes with the array in the same order that it was before the sort function.
I've tried everything I can think of. I've done module.exports.sortByField(), I've tried requiring the .sortByField() in a completely separate module. I'm sure there's a simple answer, but I just can't figure it out.
I suspect that it's working the same in both cases, but the problem is that the sort callback is incorrect.
A sort callback must return a negative number, 0, or a positive number, not a boolean: A negative number if the first entry should be before the second, 0 if they're the same, a positive number if the first should come after the second.
If cards contains numbers, you want:
return card1[fieldName] - card2[fieldName];
...if you want to sort ascending.
If it's strings, you can use localeCompare:
return card1[fieldName].localeCompare(card1[fieldName]);
So I hope I can be forgiven for being new to all this, but it turns out that the code I posted originally wasn't the problem at all. Long story short, the array that I passed to the getResult() function was an array of arrays, instead of an array of objects.
I was adding cards to an array of played cards with this code:
playedCards.push(game.playCard(1, players[0].hand, 3));
and the playCard() function was exporting an array:
exports.playCard = function(amount, hand, index) {
const cards = hand.splice(index, amount);
return cards;
};
but I should have been exporting an individual object:
exports.playCard = function(amount, hand, index) {
const cards = hand.splice(index, amount);
return cards[0];
};
I have a question about array.sort() method in JavaScript. Set of values in the array can vary from 1 to more. Sort function should sort them in the order if there is more than one element. Here is my code:
var myDates = ["03/05/2017","02/01/2017","03/02/2017"];
myDates.sort(function(a,b) {
a = a.split('/').reverse().join('');
b = b.split('/').reverse().join('');
return a > b ? 1 : a < b ? -1 : 0;
});
Code above works fine, all dates are sorted. My question is should I check the length of the array before I run the sort method? I'm asking this because my array can have one element only in some situations. So far my code did not throw any errors when I tested with one element only, but I would like to know if I should check the length of the array before I run the sort() or JavaScript already takes care of that? If anyone knows the answer please let me know. Thank you.
This behaviour is documented in the Array.prototype.sort specification. See http://www.ecma-international.org/ecma-262/6.0/#sec-array.prototype.sort
Specifically:
The arguments for calls to SortCompare are values returned by a previous call to the [[Get]] internal method, unless the properties accessed by those previous calls did not exist according to HasOwnProperty. If both perspective arguments to SortCompare correspond to non-existent properties, use +0 instead of calling SortCompare. If only the first perspective argument is non-existent use +1. If only the second perspective argument is non-existent use −1.
In short:
Array.prototype.sort((undefined, undefined) => { ... }); // => 0
Array.prototype.sort((undefined, b) => { ... }); // => 1
Array.prototype.sort((a, undefined) => { ... }); // => -1
I am new to d3 and try to learn it by reading its source code. I start with probably the simplest function d3.min(). However, my hair was torn apart by a seemingly very common code f(array[i], i, array).
I think f() is supposed to be a function;
array[i] is accessing the element of array at index i;
i is an index of the array;
array is an array of numbers given by user.
If the above 4 understandings are correct, then f() as a function given by the user, must have all three of array[i], i, array as its arguments. But we don't have to use all of these arguments, right?
What is the point of this f()? Can anyone offer any useful example/usage of d3.min(array, f) in which f is not null?
There is a little confusion here. First, d3.min and d3.max can be used with or without an accessor. In the API:
d3.min(array[, accessor])
This square bracket before the comma means that it is optional, not compulsory. So, you can have a simple:
var something = d3.max(someArray);
Or, using an accessor (as you asked, an example where the accessor is not null):
var something = d3.max(data, function(d) {
return d.foo
});
Now we come to your question: you can see in the code above that the accessor function has only 1 argument.
The example you provided is the source code D3 uses to deal with the accessor function. If you look at the code, you'll see array[i], i and array. Those 3 arguments are not provided by the user, but passed to the accessor function by the D3 source code.
Remember that in JS you can pass more arguments than parameters or less arguments than parameters.
The f is a callback function. We use callbacks all the time in JavaScript. So this function works the same way as the map or forEach method does on standard Arrays. It also has the same call signature of value, index and array.
d3.min([1,2,3], (v,i,arr)=>10-v ) // 7
d3.min([1,2,3]) //1
When we call the min function with a 'callback' like the above the answer is the third item in that array but gives the answer as 7 (because 10 - 3 is 7)
adding a f function to find the minimum value of the array while this minimum value being >= 6
var array = [10, 2, 3, 4, 5];
// find the minimum while >= 6
var f = function(x, y, z) {
if (x >= 6) {
return x;
}
}
var min = d3.min(array, f);
console.log(min);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
When I tried to run the following piece of code:
function flatten(a) {
return [].slice.call(arguments).reduce(function(acc, val) {
return acc.concat(Array.isArray(val) ? flatten.call(null, val) : val);
}, []);
}
I got the following error:
Uncaught RangeError: Maximum call stack size exceeded
But if I used flatten.apply instead of flatten.call, it works perfectly. (deep flattens the input array).
In addition to this link1 and link2, I read few other blogs and articles to understand why it behaves so. I either couldn't find the answer or I must have overlooked it. (pardon my soar eyes if in latter case)
Of course, the fundamental difference between these 2 is:
apply - requires the optional parameter be an array
call - requires the optional parameters be listed explicitly
Usually, a function may take any type of parameters - primitives and non-primitives. So, my questions are:
Could one of the optional parameters to the call method be of type Array or other non-primitive types?
Why does the above code exceeds call stack, when call is used?
Edit: Since there were 2 call methods used, my description was ambiguous. I made changes to clarify.
Your mistake is here:
[].slice.call(arguments).reduce
^^^^^^^^^
so when you pass [x], you're calling reduce on [[x]] and the first val becomes [x]. Then you call flatten once again with [x] and the story repeats itself.
On the other side, apply(..., val) will pass just x to flatten, thus reducing the nesting level by one.
If you're interested on how to use apply to flatten a deeply-nested array, this is possible without recursion:
while(ary.some(Array.isArray))
ary = [].concat.apply([], ary);
Here's a small illustration of call vs apply:
function fun() {
var len = arguments.length;
document.write("I've got "
+ len
+ " "
+ (len > 1 ? " arguments" : "argument")
+ ": "
+ JSON.stringify(arguments)
+ "<br>");
}
fun.call(null, 123);
// fun.apply(null, 123); <-- error, won't work
fun.call(null, [1,2,3]);
fun.apply(null, [1,2,3]);
When you call flatten.call(null, val), with val being an array, the flatten function receives as argument val what you passed as optional argument to call (the type isn't checked), so arguments is [val].
You see your val array is inside an array. When you call reduce, val, inside the callback, will still be an array, you flattened nothing, that's why it never stops.
When you call flatten.apply(null, val), with val being an array, the flatten function receives as arguments the elements of val, so arguments is identical to val (not [val]) : you've effectively unwrapped the array. That's why it works.
I use this script for page pagination like in this tutorial http://fdietz.github.io/recipes-with-angular-js/common-user-interface-patterns/paginating-through-client-side-data.html
app.filter('offset', function() {
return function(input, start) {
start = parseInt(start, 10);
return input.slice(start);
};
});
Everything went fine, except that I got an error
TypeError: Cannot read property 'slice' of undefined
at k.<anonymous> (http://www.foo.com/43267ztX/default/:18:17)
at e (http://www.foo.com/43267ztX/default/js/angular.min.js:171:180)
at db.| (http://www.foo.com/43267ztX/default/js/angular.min.js:160:65)
at F.constant (http://www.foo.com/43267ztX/default/js/angular.min.js:170:82)
at db.| (http://www.foo.com/43267ztX/default/js/angular.min.js:160:70)
at F.constant (http://www.foo.com/43267ztX/default/js/angular.min.js:170:82)
at Object.$watch.p (http://www.foo.com/43267ztX/default/js/angular.min.js:107:159)
at k.$digest (http://www.foo.com/43267ztX/default/js/angular.min.js:109:78)
at k.$apply (http://www.foo.com/43267ztX/default/js/angular.min.js:112:173)
at h (http://www.foo.com/43267ztX/default/js/angular.min.js:72:300) </pre>
Try:
app.filter('offset', function(start) {
return function(input) {
start = parseInt(start, 10);
return input.slice(start);
};
});
What does this mean?
Each filter must take an input and return an output.
Each filter must be built from criteria. This means, in the example: given a certain start, give me a function which takes an input and produces an output slicing (start, 10).
It's much like the decorator pattern.
Don't believe me? Read the official doc to see how filters are high-order functions (functions that return functions - this functions become criteria factories, and the resulting function is used only on the purpose of the defined function).
What were your errors?
In the wrapper function (let's say), you must only give parameters which will have use on defining the function which will be the actual filter. You will use this filter as{{ myArray|offset:3 }}, and only receive ONE parameter: (start) which will, in this case, 3.
The wrapped function will take exactly one parameter (the name does not matter).
To illustrate this even more: (editing...)
Let's make a new filter, one with more caps than yours for one parameter:
app.filter('limit', function(start, count) {
start = parseInt(start);
count = parseInt(count);
return function(input) {
return input.slice(start, start + count);
}
});
Each filter is kind of a factory (actually: it is a factory). This one takes two parameters and yields the actual filter. The actual filter is a function that takes a parameter and returns a filtered value. The inner criteria is defined by the parameters I passed to the wrapper function.
So when you use it like this:
{{ myArray | limit:5:5 }}
You say something like:
Take the arguments (5, 5).
Create a function which takes an input and slices it on (5, 10), and return it.
Apply that returned function to myArray.