javascript: apply two-dimensional array - javascript

Is there a way to apply a two-dimensional array to an object?
Like this:
var myArray = [[0,1],[2,3]];
someObject.apply(null,myArray);
It seems to apply only the first inner array :-/
Why is that?

Okay, since your question was so uninformative I'm going to assume a lot of stuff. Firstly, I'm going to assume that someObject is a function. Next I'm going to assume that it has only one formal parameter like #Adam pointed out. So this is what I assume your code looks like:
function someObject(a) {
alert(a); // were you expecting [[0,1],[2,3]]?
}
var myArray = [[0,1],[2,3]];
someObject.apply(null,myArray);
This is what I think you want instead:
function someObject() {
alert(arguments); // now it alerts [[0,1],[2,3]]
}
var myArray = [[0,1],[2,3]];
someObject.apply(null,myArray);
Remember, when you apply arguments to a function you pass it the arguments as an array. It's kind of like calling the function as follows:
function someObject() {
alert(arguments);
}
var myArray = [[0,1],[2,3]];
someObject(myArray[0], myArray[1]);
Of course, it also assigns the function a custom this pointer.
Edit: Looking back at your code I think you might have intended to use call instead of apply. The method call allows you to pass the arguments to the function as separate arguments instead of an array of arguments. So your code would look:
function someObject(a, b) {
alert(a); // now a is [[0,1],[2,3]]
alert(b); // b is 5 and so on
}
var myArray = [[0,1],[2,3]];
someObject.call(null,myArray,5);

You code works..
With that code you pass two arguments to the someObject function, [0,1] and [2,3]
According to this fiddle that is exactly what happens.
http://jsfiddle.net/BgVxQ/
Edit: If you have an unknown number of arguments, use the arguments variable available inside the function to get hold of them. If you have a fixed number of arguments, then it's often easier to declare them
function someObject(parameter1, parameter2){
//Do stuff
}
That way you don't need to manually extract them from arguments

var myArray = [[0,1],[2,3]];
someObject.apply(null,[myArray]);

According to the docs, apply takes an array of arguments and passes them to your function. So, you need to place myArray inside an array that will be unpacked to form the argument to someObject:
var myArray = [[0, 1],[2, 3]];
someObject.apply(null, [myArray]);
In the code you posted, the function someObject was receiving two arguments: [0, 1] and [2, 3]. This is legal because JavaScript allows functions to be called with a number of arguments that differs from the number of formal parameters. But because there were more arguments than formal parameters, the second argument ([2, 3]) was lost and you only saw the first ([0, 1]).

Related

Invoking .apply on .concat of array

I'm creating wrappers for common JavaScript types that offer more features. In addition to new methods and properties, they have all of the same methods and properties as the types that they are wrappers for. I'm not completely rewriting the already existing methods even though I might change their logic, so I'm always calling back to the original method on the wrapped value.
Here is what charCodeAt might look like for a wrapper around strings:
this.charCodeAt = function (index) {
return internalValue.charCodeAt(index);
};
I don't need to do much in the above example:
The parameter index in a call to string.charCodeAt is optional, so I don't need to write any logic that would handle the case of the same parameter being omitted in a call to wrapper.charCodeAt.
index is the only parameter string.charCodeAt takes, so there is also no need to handle additional variables passed to wrapper.charCodeAt as they will be ignored.
I've encountered a problem while trying to make calls to a method that accepts a variable number of arguments. The method in question is array.concat. There are other contexts in which I've written code to pass an arbitrary number of arguments to an function in JavaScript and the solution I've used is func.apply(null, argumentArray) where argumentArray is an array of the arguments I want to pass to func. This doesn't work for .concat:
var array = [0, 1];
var error;
try {
array.concat.apply(null, [2, 3]);
} catch (error) {
console.log(error);
}
The error message that gets written to the console says TypeError: Array.prototype.concat called on null or undefined. I researched using apply with concat and it seems like I'd get similar problems with other methods that are defined on a constructor's prototype.
Why am I encountering this problem with array.concat.apply? What is the correct way to pass an arbitrary number of arguments as an array to a method such as .concat?
Calling the prototype method and correctly setting the this argument in the call to apply() should work:
var array = [0, 1];
var error;
try {
array = Array.prototype.concat.apply(array, [2, 3]);
} catch (error) {
console.log(error);
}
console.log(array);
apply expects a this value of an array (or array-like) object. When you do
array.concat.apply(null, [2, 3]);
the this value is null; it's effectively the same thing as
Array.prototype.concat.apply(null, [2, 3]);
Any reference to the original array is disregarded.
If you wanted to output the concat of the original array with [2, 3], you would need to specify that the this value should be array:
var array = [0, 1];
var arrayToConcat = [2, 3];
const result = array.concat.apply(array, arrayToConcat);
console.log(result);
// or, use array spread:
const result2 = [...array, ...arrayToConcat];
console.log(result2);
// or, use concat without anything special at all:
const result3 = array.concat(arrayToConcat);
console.log(result3);
(technically, you can use a non-array-like object as the this for concat, but it's an odd thing to do)

JavaScript array as function input

I am be beginner in JavaScript, and do not know how to make a simple function with an array/vector as an input. This is the code I have:
function semGPA(credits) {
var c = credits.length;
return c;
}
I want to pass a list of numbers (ex. credits={3,4,5,6}), and have it tell me that there are 4 elements in the variable. So far, I have not found any built in functions that create a working function. Everything I try to use other than +-*/ results in a TypeError. Here it says the property "length" is undefined.
It's not entirely clear what you want. You can pass an array as credits and your function will work fine (although it's a bit pointless as is, since you can just directly call length on your array):
semGPA([1,2,3,4]); // returns 4
Perhaps you meant that you wanted to do something like this?
function semGPA() {
var c = arguments.length;
return c;
}
Which you can call like this:
semGPA(1,2,3,4); // returns 4
Which uses the arguments object which is a special object passed to functions that can be used to access all the arguments (including unnamed arguments) passed to a function.
As mentioned in the comments the example you have is an Object and not an array.
var array = [1,2,3,4,5]
var object = {1,2,3,4,5} ==> This is not a valid object in Javascript.
The object notation above expects a key-value pair. something like
var object = {1: 1, 2: 2, 3: 3}
The length property does not exist on an object. However, if you want to check the number of elements in an Object you can make use of the below code which is self explanatory:
var credits = {1:1,2:2,3:3,4:4}
function semGPA(credits) {
var creditKeysArray = Object.keys(credits); //this will give you an array of keys
var creditLength = c.length;
return creditLength;
}

Do array elements have names by default in JavaScript? [duplicate]

This question already has answers here:
Understanding Javascript callback parameters
(3 answers)
Closed 5 years ago.
var animals = ["cat","dog","fish"];
var lengths = animals.map(function(c) {
return c.length;
});
console.log(lengths);//[3, 3, 4]
Here is the code. I don't understand where this 'c' argument comes from.
I tried to change this argument to another one (any word, actually, in both places), and the console.log result is always the same!
But this 'c' is not defined anywhere! Where does 'the engine' get the value of this 'c'?
You've asked two slightly different questions. First to the question body:
I don't understand where this 'c' argument comes from. I tried to change this argument to another (any word, actually, in both places), and the console.log result is always the same!
But this 'c' is not defined anywhere!
Where does 'the engine' gets the value of this 'c'?
You define the parameter name (as you've noticed, you can choose any name for it you like). The value comes from the array, because map calls your callback and determines what argument to pass for that parameter.
Here's a conceptual implementaton of Array.prototype.map, which make make this clearer:
// CONCEPTUAL ONLY, NOT AN ACTUAL VERSION OF IT
function maplike(array, callback) {
var result = [];
for (var i = 0; i < array.length; ++i) {
result[i] = callback(array[i]);
// ^^^^^^^^--- where 'c' comes from
}
return result;
}
var animals = ["cat","dog","fish"];
var lengths = maplike(animals, function(c) {
return c.length;
});
console.log(lengths);//[3, 3, 4]
Do array elements have names by default in JavaScript?
Sort of, but not in the way you're thinking. The name of the element is its index, 0, 1, etc. In fact, JavaScript arrays aren't really arrays at all* and those indexes are converted to string property names (in theory; in practice, JavaScript engines optimize it).
* (disclosure: that's a post on my anemic little blog)
You're telling the interpreter how the parameter is called, here:
function(c) {
^
Array.prototype.map() requires a callback that accepts up to 3 parameters. The first parameter is always the "current item", which you happen to have named c.
For a more in-depth explanation, have a look at T.J. Crowders answer, as well.
In javascript, functions are first class object, which means they can be assigned to variables, passed as function parameters and returned from values. The Array.prototype.map function takes a function with it's first parameter denoting an item of the array. When invoked, the map function executes the given function for each of the items and creates a new array from the outputs of the given function.
In your case, you are defining the input function on the fly, inside the map function.
You can actually define the function outside and pass the function by reference inside map like below.
function getLength(item) {
return item.length;
}
var animals = ["cat","dog","fish"];
var lengths = animals.map(getLength);
console.log(lengths);//[3, 3, 4]
Here, you can see it outputs the same result.
The code does not know what is the parameter named. You map an array. map function creates a new array in the lengths variable (variable being assigned to). How? It provides to the function parameter inside it, each element in the current array one-by-one by value.
Here the value is actual string name ("cat" or "dog" or "fish").
In javascript, parameters can be optional. This map function can take three parameters, currentValue, index, array. In your case, c provides currentvalue.
If you would add one more parameter c,idx. Map function will get currentvalue and index inside it.
var animals = ["cat","dog","fish"];
var lengths = animals.map(function(c, idx, arr, test) {
console.log(c); // currentvalue being processed in the array.
console.log(idx); // index of currentvalue in the array
console.log(arr); // original array being operated on.
console.log(test); // undefined always. not available in map.
return c.length;
});
console.log(lengths);//[3, 3, 4]

Rewrite following piece of javascript code

I am trying to create a function that mimics Array.prototype.push.
It takes a variable number of arguments and pushes them into a specific array.
I have managed to do this with the following code:
var array=[];
function append(){
for(var i=0;i<arguments.length;i++)
array.push(arguments[i]);
}
Now my question is:Can I rewrite the append function without using "for loop"?
Thanks in advance.
If you need to get arguments array, you should use Array's slice function on an arguments object, and it will convert it into a standard JavaScript array:
var array = Array.prototype.slice.call(arguments);
You could use Array.prototype.push.apply
function append(){
// make arguments an array
var args = Array.prototype.slice.call(arguments);
// return the number of elements pushed in the array
return Array.prototype.push.apply(array, args);
}
So, what's happening here with args? We use Array.prototype.slice.call with arguments, the purpose being to make arguments an array, because it is a special object. Function.prototype.call is used to call a function with a specific context (aka this), and then the arguments to call the function with (comma separated). Conveniently, it appears that slice() looks at the length property of the this context, and arguments has one too, and when not empty, has properties from 0 to length -1, which allows slice to copy arguments in a new array.
You can rewrite this without a for loop, but you have to use a loop of some sort (you're working with multiple items, it's a necessity).
If you have access to ES6 or Babel, I would use something like:
function append(...args) {
return array.concat(args);
}
Without ES6, you need to work around the fact that arguments isn't a real array. You can still apply most of the array methods to it, by accessing them through the Array prototype. Converting arguments into an array is easy enough, then you can concat the two:
function append() {
var args = Array.prototype.map.call(arguments, function (it) {
return it;
});
return array.concat(args);
}
Bear in mind that neither of these will modify the global array, but will return a new array with the combined values that can be used on its own or assigned back to array. This is somewhat easier and more robust than trying to work with push, if you're willing to array = append(...).
Actually i honestly believe that push must be redefined for the functional JS since it's returning value is the length of the resulting array and it's most of the time useless. Such as when it's needed to push a value and pass an array as a parameter to a function you cant do it inline and things get messy. Instead i would like it to return a reference to the array it's called upon or even a new array from where i can get the length information anyway. My new push proposal would be as follows;
Array.prototype.push = function(...args) {
return args.reduce(function(p,c) {
p[p.length] = c;
return p
}, this)
};
It returns a perfect reference to the array it's called upon.

function executed by .apply() works with array, but does not the original function

if Math.max([1,3,9]) returns error (needs a list of numbers, not an array), why calling it via apply like below works?
function getMaxOfArray(numArray) {
return Math.max.apply(null, numArray);
}
getMaxOfArray([1,3,9]) //9
getMaxOfArray(1,3,9) //error
I understand .apply passes an array, but why should max function work with them only when called via apply? is there some internal transformation array => list ?
apply expects the parameters to be in an array. if you just have the parameter list how you do in the second case, use call instead
Math.max.apply(null,[1,3,9])
Math.max.call(null,1,3,9)
What is the difference between call and apply? goes into a good amount of detail on the difference between call and apply
Your function only accepts one argument (numArray), not three—this is why your call to getMaxOfArray is failing. If you are writing a one-liner, you should use call instead of apply for a series of parameters rather than an array, as so:
Math.max.apply(null, [1, 3, 9]);
Math.max.call(null, 1, 3, 9);
For a function, you can use the arguments object for a variable number of parameters, if you do not want the user to pass an array. Here's how you would go about doing it this way. (Note that I still call apply here, because I store all of the arguments called by the user into an array.)
function getMaxOfArguments() {
var parameters = 1 <= arguments.length ? [].slice.call(arguments, 0) : [];
return Math.max.apply(null, parameters);
}
Now, your second function call should work:
getMaxOfArguments(1, 3, 9); // 9
The function signature of Math.max is function(arg1, arg2, arg3, ...) not function(Array). Apply is converting the input into a more palatable form for Math.max.

Categories