i have a function in javascript where i need to retrieve the last element of the list. the list can be an array, string, list in numbers (not array). I tried converting the list into a String and then an array and retrieving it by index, but that's not working.
Here is the code I tried:
function last(list){
var array = new String(list);
array = array.split("");
return array[array.length-1];
}
I don't understand what the problem is because test suite says Expected: 5 instead got: 5
I am using code wars and did not write the tests. Is it expecting a Number and getting a String '5' ? I don't understand types in loosely typed languages very well yet.
from the comments, I think you mean you want to either return the last element in an array, the last character in a string, or the last argument passed if multiple arguments were passed. This would do it:
function last() {
if (arguments.length > 1) { // first we handle the case of multiple arguments
return Array.prototype.pop.call(arguments);
}
value = arguments[0]
if (typeof value === 'string') { // next, let's handle strings
return value.split('').pop();
}
if (Object.prototype.toString.call( [] ) === '[object Array]') {// Arrays are of type object in js, so we need to do a weird check
return value.pop();
}
}
arguments is a pseudo-array that contains all arguments passed into the function, so for last(1,2,3,4,5), arguments would be roughly [1,2,3,4,5]. It's not exactly that though, because arguments has all args in order and a length property, but it's prototype isn't Array, so it isn't truly [1,2,3,4,5] and lacks all of array's functions. This is why we need to call pop in the context of arguments (in javascript, Function.prototype.call calls the function passing the first arguments as the value of this, and all the rest of the arguments into the arguments pseudo-array, so for example last.call([], 1, 2, 3) would call last in the context of a new array and with arguments roughly equal to [1,2,3]).
the rest of the code is pretty straightforward, except for the check to see if value is an array, which is further explained here.
Finally, pop is an array method that removes the last element from an array and returns it.
Related
As I was looking at a unique() function I found
which takes an array as argument and returns a new array which contains the unique elements of this array (which means no duplicated items). However I cannot understand the logic of this function. Can somebody explain it to me?
Here is the function:
function unique ( array ) {
return array.filter(function(a){
return !this[a] ? this[a] = true : false;
}, {});
}
I can't really understand the whole code especially the !this[a] ? this[a] = true : false; and the new object ({}) that is passed as the second argument to filter.
Let's start with the filter:
The filter() method creates a new array with all elements that pass
the test implemented by the provided function.
The a is the random number of the array to which you apply the filter. The whole essence is in the following statement:
return !this[a] ? this[a] = true : false;
If the this[a] is true, a has been already processed once and it has been added to this as one of its properties. Otherwise, this[a] is false. So taking its negation result in true and the current a should be returned. Furthermore this[a] would be set to true and then we proceed to the next a.
The following snippet will help you grasp what filter does:
var numbers = [1,2,3,4,5];
var filteredNumbers = numbers.filter(function(number){
console.log(number);
return number > 2;
});
console.log(filteredNumbers);
And the following snippet will show you in action what happens in unique function:
function unique ( array ) {
return array.filter(function(a){
console.log(this);
return !this[a] ? this[a] = true : false;
}, {});
}
var array = [1,2,3,1,2,3,4,5,5,6];
console.log(unique(array));
I understand the basic logic of filter but what i dont is the {}
passed as a 2nd argument and how each value is added to a new array
with !this[a]
The second argument is an optional value that you can pass to the filter method and it can be used as the this, when your callback would be executed (check the link I mentioned at the beginning about filter). You pass there an empty object. When you use the keyword this inside your callback, your refer this object. This is why the first time that code gets in this method returns {}. Check the first line of the output of the second snippet.
I will explain the second part of your question based on the second snippet. The first time you get in you have an empty object (I refer to this) and the first number processed is 1. So this1 would be undefined. So !this[1] would be true. Hence the first part after the ? is executed which is an assignment
this[1] = true.
Now this acquired its first key, 1, with value true. Furthermore, 1 would be returned from filter. The same happens with 2 and 3. When we arrive at 1 the
!this[1]
is false, since this[1] is true. So false is returned and the 1 now would not be added to the array that would be returned after all elements of array have been processed.
Basically, .filter would call the callBack function by supplying the individual values of the iterating array. If the callBack returns a value that resolves to true then that value will be collected, else that particular value will be ignored.
Here the second argument of filter has been used. That second argument will be used as a context(this) while calling the callBack internally. So here in your code, the passed object will be added with the array's value as property for each iteration. And in the consecutive iterations, the code will check the current value is available as a property in the initially passed object. If available then that ternary operator would return false, otherwise true.
Hence the unique values will be returned from the filter function.
Array.filter will only grab elements of the array when the function passed return truthy.
For each element of the array it is doing
return !this[a] // if value is not yet on this
? this[a] = true // add value to this and return true (grab element)
: false; // value was already cached, so return false (don't grab)
So it will only return 1 of each
Other answers have explained basically how this works. But a couple of points:
First, instead of return !this[a] ? this[a] = true : false;, it would be more concise to write
!this[a] && (this[a] = true)
Second, this code has the flaw that it only works on elements which can serve as keys into an object--basically strings or numbers. That is why using Set is better:
Array.from(new Set(array))
The above will work on any primitive or object.
Third, this approach does not work properly with strings and numbers. If the number 1 is present, it will filter out the string "1".
const uniq = a => a.filter(x => !this[x] && (this[x] = true), {});
console.log(uniq([1, '1']));
The reason is that object keys are string-valued.
Finally, IMHO this code is a bit too tricky for its own good. Most developers, even experienced ones, would stop and scratch their heads for a minute before figuring it out. Less experienced developers would have to consult the documentation for the thisArg parameter to Array#filter before being able to understand it. The ternary operator with the assignment inside is also a bit cryptic. I'd go ahead and write it out as
if (this[x]) return false;
this[x] = true;
return true;
I've found something interesting and I don't know why it's happening.
If I try in google chrome developer tools, the following two staments
(Array([1,2,3])).filter(function (item, index, array) {
return item === 1;
}); ==> []
and
([1,2,3]).filter(function (item, index, array) {
return item === 1;
}); ==> [1]
The results are an empty array for the first statement and array with a single value (1) for the second
Inspecting the parameters for the callback function, i found that in the first statement the arguments are (array, index, value) and for the second statemente are(value, index, array).
Inspecting with typeof and constructor of both objects the result are the expected, and the same "object", and Array.
Why is this happening?
Thanks
Because that's not how you define an array with Array().
Should be without the square brackets, otherwise it's an array of a single element, which is also an array ([1,2,3]).
Array(1,2,3)
That inner array never equals to 1 (basically you check [1,2,3] == 1), so the result is an empty array.
If you define an array by using Array([1,2,3]) this code, then the following array will be created,
[[1,2,3]]
Since you are pushing an array into another one. And if you really want the Array function to create an array by reading an array then you have to write something like this,
Array.apply([], [1,2,3])
But the above one is completely pointless. Again I am telling it is completely pointless since we are having an array that we require in our hand. But just for a knowledge you can know about it.
Array([1,2,3]) create array of arrays [[1, 2, 3]] so .map()function will iterate one time only.
If you want to create array with Array constructor use next syntax:
Array(1,2,3)
the shorter is the better :
[1,2,3].filter(item => item === 1);
In going through some exercises about functional programming, and returning a closure, came across this example, and cannot figure out how it works:
function invoker (NAME, METHOD) {
return function(target) {
var targetMethod = target[NAME];
var args = _.rest(arguments);
return function() { return targetMethod.apply(target, args);});
};
};
The test:
var rev = invoker('reverse', Array.prototype.reverse);
var result= _.map([[1,2,3]], rev);
The output should be:
[[3,2,1]]
but i cannot prove it.
Specific questions:
Why is args:
0,1,2,3
Why is console.log(arguments):
[object Arguments]
How to print out the result, to see [[3,2,1]].
When I print out "result", I get:
[object Object]
After correcting the issues in the code (missing semicolons or too many) there appear to be a few closures that get created throughout the script's execution.
First:
function invoker (NAME, METHOD) {
return function(target) { // In the example this is rev
// Begin Scope1 (closure)
var targetMethod = target[NAME]; // NAME is trapped here in Scope1 (defined outside of this function) when the function is executed!
var args = _.rest(arguments);
return function() { //
// Begin Scope2 (closure)
return targetMethod.apply(target, args); // target and args are trapped here in Scope2 (defined in Scope1's function) when the function is executed!
};
};
}
The invoker does nothing until it is called and the first closure (Scope1's that is) does not get created until invoker is actually invoked:
var rev = invoker('reverse', Array.prototype.reverse);
After rev has been initialized by invoker, it has become a variable that contains a function that accepts a single named argument (called target) that when called will look for a function called NAME (in this case 'reverse') on whatever object target is.
When rev is actually called it also returns a function that, when called, will call the target method (reverse in this case) on target (the array passed in). But rev hasn't been called until the next line is run:
var result= _.map([[1,2,3]], rev);
Here is what is happening here: _.map's purpose is to take a list of items and apply a function to each item in this list. The final result will be a new array with the transformed values in it. The line above passes to _.map a list with exactly one item in it; an array. It also passes to _.map a function to transform each item in that list; in this case rev.
So _.map is called and it calls rev on the only item in the list, our [1,2,3] array. The function rev has the effect of returning yet another function that, when called, will remember NAME, target and args. This function now resides inside the first element of the array variable 'result'.
Now for your questions:
The output should be:
[[3,2,1]]
but i cannot prove it.
In the test code there is no output, only a final variable called result. What I think you are looking for is that somewhere in the end there should be a reversed array. You can verify that the original array has been reversed with the following after the call to initialize result:
alert(result[0]().join());
Why is args:
0,1,2,3
args is really a red herring here; _.map passes 3 arguments to its iterator function. The current value (in this case the array [1,2,3], the key or index (in this case 0) and the original list itself. When _.rest is called on arguments, it slices off the first item leaving us with an array containing 0 and [[1,2,3]]. When reverse is called on this array (args) it ends up returning an array that looks like [1,2,3,0] which is these two items reversed. My tests never showed 0,1,2,3. Why is args a red herring here? Because when reverse is called reverse doesn't take any arguments and so args is ignored. If this was a different function on a different object you would probably encounter issues because of the call to _.rest.
Why is console.log(arguments):
[object Arguments]
Because you are basically calling toString() which will print the object's type.
How to print out the result, to see [[3,2,1]]. When I print out
"result", I get:
[object Object]
You can use:
console.log(result[0]().join());
UPDATE: Here is the jsbin link with working code: http://jsbin.com/aYECIDo/5/edit?html,css,js,output
Use console.log and a modern browser with actual development tools.. like chrome, or firefox+firebug. Then you should be able to see in the console the result. Or, when you print it out in HTML, serialize it using JSON.stringify. Example: document.write(JSON.stringify(result)).
That should write out a JSON representation of what you're seeing.
Regarding the [1,2,3] look at the .apply function in the MDN.
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/apply
Apply takes a first argument of scope, and an array of arguments to cause the function to be called with.
function Foo(){
}
Foo.prototype={
method1:function(o){
console.log(o);
}
,shorthand:function(){
if(!arguments.length || typeof arguments[0]=='undefined') {
arguments[0]={};
}
arguments[0].bar='test';
return this.method1.apply(this,arguments);
}
}
var instance=new Foo();
instance.shorthand();
console.log(o); // returns undefined
After a while i figured out, that assigning arguments an array fixes this.
arguments=[{bar:'test'}];
I figured out, that arguments is not an array after all, or semi-array. Well it doesn't have push method.
Why is it doing so(i mean returns undefined)? It's been made for some purpose?
You already have seen that arguments is not an array, but only "array-like". It is a normal object which has numerical keys and a length property:
Your code
arguments[0]={};
arguments[0].bar='test';
does work in some way.
But because it is not an array, the length property is not updated. So although you added a property, length is still 0:
Now, I don't know the implementation of apply but I assume it uses the length property to iterate over all elements in the "array". If length is zero, it does not pass any arguments to the function, so o in this case is indeed undefined.
If you add
arguments.length = 1;
then it works as you expected.
Assigning an array to arguments just overwrites the object and assigning elements to an array updates length as it is known.
I have an associative array with two object inside. Running this through $(myassoc).each(), the callback runs only once. Also the callback parameters (index and object) returns 0 and the entire associative array, respectively.
One would expect jQuery.each() to run for each element in the array, returning the correct keys as index and the correct element as the object.
Why isn't that happening, and can jQuery do what I'm after?
I think you're looking for jQuery.each() instead of .each()
try this:
$.each(myassoc, function(index, value){
//your code
});
try this:
$.each(assocarray,function(i, value){
console.log('index: ' + i + ',value: ' + value);
});
Badly.
Don't $(associative_array).each(function () {...}) -- that's nonsense
Don't $.each(associative_array, function() {...}); -- that has an obscure bug(1)
To see the bug, try this in a javascript console:
> $.each({foo:1, length:-1, bar:2}, console.log)
foo 1
length -1
bar 2
> $.each({foo:1, length:0, bar:2}, console.log)
The first example outputs three lines of key-value pairs, as it should. The second outputs nothing!
The moral of the story, don't use jQuery.each() on objects. (Objects in JavaScript are essentially the same thing as associative arrays.) Things may work fine forever, but you run the risk that someday an object happens to have a member named length and its value happens to be exactly 0 and then you have a bug out of nowhere that can be very difficult to explain. (I'll let you guess, by the ponderous heft of this answer, whether that ever happened to me.)
As mentioned in the bug report:
If you need to iterate over all keys of objects having the length property, jQuery.each is not the correct solution.
I suggest going further, that jQuery.each should not be relied upon for associative arrays, ever.
(1) This "bug" may never be fixed, since $.each() historically uses Duck Typing on arrays: "Arrays and array-like objects with a length property (such as a function's arguments object) are iterated by numeric index."
Here's what I use[thanks Dominik] to loop through property names and values of objects, or put another way, the keys and values of an associative array:
function looper(object, callback) {
for (var key in object) {
if (object.hasOwnProperty(key)) {
if (false === callback.call(object[key], key, object[key])) {
break;
}
}
}
return object;
}
looper() is then a drop-in replacement for $.each()
> looper({foo:1, length:0, bar:2}, console.log)
foo 1
length 0
bar 2
Just like $.each():
Inside the callback, this is each value
Inside the callback, returning false (not just falsy) terminates the loop
looper() returns the object originally passed to it
looper() works on arrays as well as objects.
Use:
var a = [];
looper({foo:1, length:0, bar:2}, function(k, v) {
a.push(k+"="+v);
});
console.assert("foo=1,length=0,bar=2" === a.join());
Try that with $.each() and you'll get an empty result. Because it interprets this particular object as an array-like object of zero length.
The problem is that the $.each() function internally retrieves and uses the length property of the passed collection. But in an associative array that has no integer indices the length always seems to be 0. For $.each() now there seems to be nothing to walk through.
The $.each() function internally retrieves and uses the length
property of the passed collection.
The solutions is simply to use an object instead.
var obj = {
"flammable": "inflammable",
"duh": "no duh"
};
$.each( obj, function( key, value ) {
alert( key + ": " + value );
});