Arguments Array in JavaScript (ES5) - javascript

Maybe that is not my evening :/ Very simple thing, I want to give an array as parameter to a function as arguments array:
function add() {
var sum = 0;
for (var i = 0; i < arguments.length; i++) {
sum += arguments[i];
}
return sum;
}
The following works:
console.log(add(1,2,3,4,5,6));
But if I fill an array and give it as parameter, like:
var myNumbers = [];
for (var i=0; i<100; i++){
myNumbers.push(i);
}
console.log(add(myNumbers));
I get trouble. I think, I miss something important about the arguments array.
How should I change the add function, so that both possibilities can work with it?

This solution works for both situation :
function add() {
var arr= Array.prototype.slice.call(arguments);
arr = [].concat.apply([], arr);
var sum = 0;
for (var i = 0; i < arr.length; i++) {
sum += arr[i];
}
return sum;
}
Also simple solution for sum :
function add() {
var arr= Array.prototype.slice.call(arguments);
arr = [].concat.apply([], arr);
return arr.reduce(function(f, s){return f + s;}, 0);
}

The arguments object is an array-like object, but it is not an array. It is used to represent all arguments passed into the function. You have only passed in one value into the function, so your array is actually at index 0 of the arguments object.
However, there really isn't much point using arguments here unless you need to dynamically handle things without defining an explicit API. Just declare your parameter in the add function.
function add(arr) {
var sum = 0;
for (var i = 0; i < arr.length; i++) {
sum += arr[i];
}
return sum;
}
If you want to support both cases, as per your comment, you can do something like:
function add() {
var arr = [].concat.apply([], arguments);
var sum = 0;
for (var i = 0; i < arr.length; i++) {
sum += arr[i];
}
return sum;
}
Explanation of
[].concat.apply([], arguments)
[]. is shorthand for Array.prototype because it's an empty array.
concat merges two or more arrays together, or an array and values to go into the array.
arguments is not an array, but many of the prototype functions will work on it, due to some array-like characteristics of the object - indexed items and the length property.
apply calls a function with a given context (the this binding) and any number of arguments. In this case we still want to use the array as this value to be able to call concat, followed by all the arguments we passed into add. The result is simply all arguments as a proper array.

Here:
add(1,2,3,4,5,6);
...you're calling add with a series of discrete (separate) arguments, and each of them shows up in the arguments pseudo-array, 1 at arguments[0], 2 at arguments[1], etc.
But here:
add(myNumbers);
...you're calling add with one argument, which is an array. That's at arguments[0].
You'll want to write add to fit how you want to call it. If you want to call it with discrete arguments, write it the way you have. If you want to write it to accept an array, have it take a single argument and loop through that (which will be the array):
function add(args) {
// ^------------- the one argument
var sum = 0;
for (var i = 0; i < args.length; i++) {
sum += args[i];
}
return sum;
}
If you want to handle both, you could use Array.isArray (newish, but shimmable) to check the first argument to see if it's an array and either use it if it is or use arguments if it isn't:
function add(args) {
if (!Array.isArray(args)) {
args = arguments;
}
var sum = 0;
for (var i = 0; i < args.length; i++) {
sum += args[i];
}
return sum;
}
Side note: arguments isn't an array, it's just array-like.

You can use apply
add.apply(null, [1,2,3])

Related

How to loop over arguments passed via the spread operator in JavaScript?

I'm writing a sum(...numbers) function which takes in a bunch of arguments via the Spread operator and adds them. I want to pass an array of values to this function via the returnsAnArray() method, as shown below
function sum(...numbers) {
let sum = 0;
for (i = 0; i < numbers.length; i++) {
sum += numbers[i];
}
return sum;
}
function returnsAnArray() {
return [1,2,3,4];
}
console.log(sum(returnsAnArray()));
This outputs 01,2,3,4 on the console.
From what I understand the ... operator collects all the arguments passed to it and makes them available as an array. If the argument is a singular array then it the array is provided as is. But this does not behave that way.
Any ideas on how I can properly use loops here to get this working as expected?
A spread syntax allows an iterable such as an array expression or string to be expanded in places where zero or more arguments for functional calls are expected. Here is the MDN link
What the above means is that if you have a function that looks like the following
function sum(x, y, z) {
return x + y + z;
}
so the above function accepts three arguments, now when you pass in an array of length 3 using the spread operator, it replaces the values x, y, z with numbers[0], numbers1, numbers[2] respectively.
So you would be calling the above function such as
console.log(sum(...numbers));
function sum(x, y, z) {
return x + y + z;
}
const numbers = [10, 12, 13];
console.log(sum(...numbers)); //output = 35
As mentioned in the comment
to explain the code that you have in place, the ...numbers in the function sum means, there is no defined number of arguments, instead accept as many as passed. Now you are calling the function sum with a single argument of type array, so this is interpreted as the first parameter to the function sum to be of type array and not what you expect/intend.
Now In order to fix or achieve what you intend to do you got to spread the array that you are passing in as argument along the lines of
function sum(...numbers) {
let sum = 0;
for (i = 0; i < numbers.length; i++) {
sum += numbers[i];
}
return sum;
}
function returnsAnArray() {
return [1,2,3,4];
}
console.log(sum(...returnsAnArray()));
If the argument is a singular array then it the array is provided as is.
No, you will get an array with an array. You have to spread the array to get what you expect:
sum(...returnsAnArray())
What the ... operator does when used in a function's signature is allow for a dynamic number of arguments, and wraps all the given arguments in an array, somewhat similar to what arguments is, this, however, is usable in arrow functions and it is more readable and that arguments is an array-like object :
function fn(...args){
console.log(args);
console.log(arguments);
}
fn(1,2,3,4);
fn('s',2,'3');
fn([1,2,3], [1,2,3]);
So, as you might have noticed in the previous snippet when you pass arrays as arguments to a function that is using ... they become part of the array, not the array itself.
Therefore, for your specific case, there is no need for the use of ... as proven by the snippet below.
function sum(numbers) {
console.log(numbers);
console.log(arguments);
let sum = 0;
for (i = 0; i < numbers.length; i++) {
sum += numbers[i];
}
return sum;
}
function sumWithDynamicArgs(...numbers) {
console.log(numbers);
console.log(arguments);
let sum = 0;
for (i = 0; i < numbers.length; i++) {
sum += numbers[i];
}
return sum;
}
function returnsAnArray() {
return [1, 2, 3, 4];
}
console.log(sum(returnsAnArray()));
console.log(sumWithDynamicArgs(...returnsAnArray()));
From what I understand the ... operator collects all the arguments passed to it and makes them available as an array.
I believe you are confusing spread with rest operator.
Rest wraps the rest of the arguments sent to a function in an array, which is exactly how you used it in your snippet.
I made use of your snippet to use spread to pass the arguments to sum function, which is exactly like: sum(1, 2, 3, 4) (console.log line).
Now, the sum function makes use of rest and takes all the values passed and wraps it in an array called numbers.
function sum(...numbers) {
let sum = 0;
for (i = 0; i < numbers.length; i++) {
sum += numbers[i];
}
return sum;
}
let numbers = [1,2,3,4]
console.log(sum(...numbers));
If the sum function is intended to receive one array of numbers each time, you should just get rid of the spread operator.
If you want the function to accept several arrays, then try something like this:
function sum(...args) {
let sum = 0;
for (let i = 0; i < args.length; i++) {
for (let j = 0; j < args[i].length; j++) {
sum += args[i][j];
}
}
return sum;
}
console.log(sum([0, 1, 2, 3]));

Why calling a function inside another function doesn't work?

var mult = (function(){
var cache = {};
var calculate = function(){
var a = 1;
for(var i = 0, l = arguments.length; i < l; i++){
a = a * arguments[i];
}
return a;
}
return function(){
return calculate(arguments);
}
})();
console.log(mult(1, 2));
Above is my code, I expect the mult function will give me value 2, but instead it outputs NaN. I changed the line calculate(arguments) to caculate.apply(null, arguments) and it worked. I don't know why the old code doesn't work? Why do I need to use apply in this case? What does null represent here?
Your calculate function wants separate arguments, but you passed in an array1. Using .apply spreads the content of the array for you.
1 Technically an array-like arguments object that does not inherit from Array.

More efficient array search

I'd like to pull a particular object from an array of objects based on a unique property of that object (ie, a key).
In the following, I'm searching for an element in 'arr' where the key is 8.
var myElement = arr.filter(function(element) {
return element.key === 8;
});
This works, but every time this runs, it will iterate through all elements in the array, even after the correct element has been found. For example, if it finds myElement at index 4, but there are 100 elements in the array, the snippet is running 25x more than it needs to.
Is there a more efficient way, or a way to terminate filter() when it has found myElement?
I feel like I've missed something obvious...
You actually want a find function that returns when the first occurrence is found. You can use Array.prototype.find if you are on ECMAScript 6 spec or you can implement a simple find like:
function find8(arr) {
// type checks skipped for brevity
var i = 0,
j = arr.length;
for (; i < j; i++) {
if (arr[i].key === 8) {
return arr[i];
}
}
throw new Error('Not found');
}
If you want a more generic solution that accepts predicates (i.e. functions that return a boolean value), you can write a more generic function like:
function find(arr, predicate) {
// Again, type checks skipped for brevity
var i = 0,
j = arr.length;
for (; i < j; i++) {
// Note the condition is now a call to the predicate function
if (predicate(arr[i])) {
return arr[i];
}
}
throw new Error('Not found');
}
for (var i=0; i<arr.length; i++) {
if (arr[i].key === 8) {
console.log('FOUND ITEM AT KEY '+8);
break;
}
}
It sounds like you'd be better off with something like this:
function findByKey(items, key) {
for (var index = 0; index < items.length; index++) {
var item = items[index];
if (item.key === key) {
return item;
}
}
return null;
}
Note that if your list is sorted, you can improve upon that by doing a binary-search instead. Though personally I'd suggest hashing (assuming that the same key isn't used twice). Something like:
var array = [/* your data here... */];
var map = {};
//do this once; or once each time your array changes
for (var index = 0; index < array.length; index++) {
var item = array[index];
map[item.key] = item;
}
//now find things by doing this
var itemWithKey8 = map[8];
I'd handle this with a simple for loop (used a function for generic re-use):
function findObject(arr, cond)
for (i = 0; i < arr.length; i++) {
if (cond(arr[i]){
return arr[i];
}
}
}
// now call fincObject(myArray, function(element){element.key === 8})
Or, if you know you are going to do this many times, create a mapping which should be a lot faster:
function makeMapping(arr, keyFunc){
var mapping = {};
for (var i = 0; i < arr.length; i++) {
mapping[keyFunc(arr[i])] = arr[i];
}
return mapping;
}
This returns an object mapping 8 to the object with key.id === 8. Your keyFunc would be:
function keyFunc(element){
return element.key;
}
Why reinvent the wheel? There are a lot of similar answers here, so I'll share a different approach--by far my favorite approach. There is an excellent library called linq.js (both standalone and jQuery plugin versions) which makes searching, filtering, sorting, etc. a breeze.
var myElement = Enumerable.From(arr).FirstOrDefault(null, function(element) {
return element.key === 8;
});
In the above example, the first element that matches the conditions is returned. If nothing is found, null is returned (the first parameter is the default value to return).

Use 3 arrays as function parameter in js

i want use some arrays as function parameters.
i write this code but doesn't work.
please help me
and i have no idea about number of array
function func2(x,y,z)
{
alert(x[1]);
alert(y[1]);
alert(z[1]);
}
function func1()
{
a = [1,2,3]
b = [3,4,5]
c = [5,6,7]
func2(a,b,c);
}
Your func2 function is calling itself. Also, using var is a good idea here to avoid accidentally creating global variables:
function func2()
{
var a = [1,2,3],
b = [3,4,5],
c = [5,6,7];
func1(a,b,c);
}
Update given your updated question, if you want to create a function that accepts a variable number of parameters, you'll need to access the arguments object:
function func2()
{
alert("There are " + arguments.length + " arguments");
}
The arguments object is can be accessed just like an array (although it is not actually an array). So to print the second element from each array (remember array indexes start at 0), you'd use something like this:
function func2()
{
for (var i = 0; i < arguments.length; i++)
alert(arguments[i][1]);
}
An alternate strategy would be to just accept an array of arrays (also called a multidimensional array, though technically multidimensional arrays aren't supported in JavaScript) like this:
function func2(a)
{
for (var i = 0; i < a.length; i++)
alert(a[i][1]);
}
function func1()
{
var a = [1,2,3],
b = [3,4,5],
c = [5,6,7];
func2([a,b,c]); // notice the […] around the parameters
}
function func1(x,y,z)
{
alert(x[1]);
alert(y[1]);
alert(z[1]);
}
function func2()
{
var a = [1,2,3];
var b = [3,4,5];
var c = [5,6,7];
func1(a,b,c);
}
If I read you minds well, you have to use arguments in order to iterate over a dynamic number of arguments. Assuming you pass only arrays to func2:
function func2() {
for (var i = 0; i < arguments.length; i++) {
alert(arguments[i][1]);
}
}

How to iterate an array in javascript

I have an array like var arr = { 30,80,20,100 };
And now i want to iterate the above array and add the iterated individual values to one function return statement for example
function iterate()
{
return "Hours" + arr[i];
}
I had tried in the following approach
function m()
{
for(var i in arr)
{
s = arr[i];
}
document.write(s);
}
The above code will gives only one value i.e. last value in an array. But i want to iterate all values like
30---for first Iteration
80---for second Iteraton
Any help will be appreciated
Iterate over using the length property rather than a for ... in statement and write the array value from inside the loop.
for (var ii = 0, len = arr.length; ii < len; ii++) {
document.write(arr[ii]);
}
That's because your write statement is outside the loop. Don't you want it like this?
function m(arr) {
for (var i = 0; i < arr.length; i++) {
s = arr[i];
document.write(s);
}
}​
Also, don't use in because it will give you ALL the properties of array. Use array.length

Categories