This question already has answers here:
Imperative vs Declarative code [closed]
(3 answers)
Closed 4 years ago.
I have a vague understanding of what does it mean "provide immutability" when talking about using map over for loop. Because calling Array.prototype.map on array does not prevent you from altering original array. Let's say we have single threaded asynchronous program. In these circumstances is there an "immutability" benefit that map provides over for loop?
I can imagine the following code:
function A() {
const arr = [1, 2, 3, 4, 5]
const anotherArr = arr.map((e, i) => {
arr[i] = 'changed'
// here goes some asynchronous code
return e * 2
})
// now arr is ['changed', 'changed', 'changed', 'changed', 'changed']
// and anotherArr is [2, 3, 6, 8, 10]
}
function B() {
const arr = [1, 2, 3, 4, 5]
const anotherArr = []
for (let i = 0, len = arr.length; i < len; i++) {
anotherArr[i] = arr[i] * 2
arr[i] = 'changed'
// here goes some asynchronous code
}
// and again:
// now arr is ['changed', 'changed', 'changed', 'changed', 'changed']
// and anotherArr is [2, 3, 6, 8, 10]
}
I guess that probably in function B anotherArr is created manually before populating. Therefore something in the same scope can alter it before for loop is completed. So my question is: "Is there a benefit of using map over for loop in single threaded asynchronous environment in terms of safety"?
Editing
I rephrase my question: "Is there some kind of immutability that Array.prototype.map function
provides (because it belongs to functional paradigm)".
As I understand now the map function in javascript is not about immutability,
it is about hiding some implementation details (how resulting array is constructed).
So map function is a delclarative way of doing things. for loop is an imperative
way of doing things (which brings more freedom and more responsibility I guess).
map, reduce, filter, forEach, etc.
Methods above, in my mind, is nice for FP(functional programming).
Of course, you can use them instead of normal for loop,
but normal for loop is faster than them(https://hackernoon.com/javascript-performance-test-for-vs-for-each-vs-map-reduce-filter-find-32c1113f19d7).
So in my idea, if your don't care performance, use map, reduce, etc. is elegant and will get lesser and more readable code.
Related
This question already has answers here:
Explanation of `let` and block scoping with for loops
(5 answers)
let keyword in the for loop
(3 answers)
Closed 2 years ago.
I am quite confused about the closing behaviour of functions declared inside of for loops, in particular about variables defined in initializers:
function createFunctions(){
const functions = []
for(let i = 0; i < 5; i++)
functions.push(() => i);
return functions;
}
const results = createFunctions().map(m => m())
// results: [0, 1, 2, 3, 4]
vs
function createFunctions(){
const functions = []
let i;
for(i = 0; i < 5; i++)
functions.push(() => i);
return functions;
}
const results = createFunctions().map(m => m())
// results: [5, 5, 5, 5, 5]
Since the anonymous arrow function declared in the for loop captures its scope, I would expect both cases to yield [5, 5, 5, 5, 5], since at call time, i's value is 5. The first result however seems to suggest that on each iteration through the loop, i is a different variable. However, if you repeat the test but the initialized variable is an object instead of a number:
function createFunctions(){
const functions = []
for(let obj = {}, i = 0; i < 5; i++)
functions.push(() => obj);
return functions;
}
const results = createFunctions().map(m => m())
// results: [{}, {}, {}, {}, {}]; results[0] === results[1]: true
we can see that all elements in the returned array are referentially equal, and so are not different variables. So it seems that the way that functions close over variables declared in the for loop initializer changes depending on whether the variable is a primitive or not, which sounds preposterous to me.
What am I missing?
So, I went through all the trouble of writing this question, and while researching I found the answer somewhat by accident, so I thought I'd switch this to a self-answer question so that if others encounter the same behaviour, it may be easier to find.
I found the answer here
In loops, you get a fresh binding for each iteration if you
let-declare a variable. The loops that allow you to do so are: for,
for-in and for-of.
Which explains why the objects are equal: we get a new variable which gets assigned to the value of the previous one at the end of the loop. In the case of the object, the new variable is pointing to the same object, which explains the referential equality. It also explains why the first case yields [0, 1, 2, 3, 4]: numbers are values, not references, unlike objects.
I was testing a few pieces of code out, and two statements that should return the same value return different values.
const arr = [3, 'foo', { bar: 'baz' }, false, 4, 5];
for(var i = 0; i < arr.length; i++) {
i = arr[i]
console.log(i)
}
returns [3, 4, 5], while
const arr = [3, 'foo', { bar: 'baz' }, false, 4, 5];
for(var i = 0; i < arr.length; i++) {
console.log(arr[i])
}
returns [3, 'foo', { bar: 'baz' }, false, 4, 5]
Why?
This is a great example of both the flexibility and pitfalls of imperative programming.
tl;dr you shouldn't be modifying i in you're for loops, and you also shouldn't be using for loops. A better way to iterate over the array is:
arr.forEach(function(item, i) {
console.log(item)
})
Imperative Programming
Copied from Wikipedia: In computer science, imperative programming is a programming paradigm that uses statements that change a program's state.
What does this mean?
In simple terms, whenever you are modifying variables (mutation), you're programming imperatively. For example:
var x = 1;
x = 5;
console.log(x + 5); // you would get 10
However, this can lead to unexpected problems which we can see in your example which is explained pretty well in other answers.
This is why in general, it's a good idea to avoid it (at least in Javascript) even though imperative programming is so convenient. This doesn't mean we always avoid it. Sometimes, programs need to run as efficient as possible, and imperatively modifying variables is the only way to achieve that.
In most cases, we don't need that efficiency if we're iterating (looping) through only a few hundred or even tens of thousands of variables. Our computers are fast enough that the difference in performance is not worth it.
Functional Programming
Functional programming is a great alternative. In practice, it boils down to one simple rule - no side effects. Side effects simply mean mutation, i.e modifying variables.
This avoids bugs that would be introduced in imperative programming since you know that once you initialize a variable, it will always be the same.
Below, I'll outline a pseudo introduction to functional programming and how it relates to your example. If you want to learn more about it, you can find countless resources online. Here's a pretty good article about functional programming with Javascript.
Some examples of functional programming
const x = 5; // we use const do denote x is constant and can't be changed ever
// x = 5 would give us an error!
const y = x + 5
// for (const i = 0; i < 10; ++i) is not functional since i is modified!
How should we loop without for loops? Recursion!
It's almost always good to avoid using for loops in the form of for (var i ...). This is because those loops are based on imperative programming, which we want to avoid if possible.
How do we print all the values of an array?
function printValues(arr, i = 0) {
if (i == arr.length) return; // exit when we reach the end
console.log(arr[i]); // let's call console.log
printValues(arr, i + 1); // do the same thing with the next index of the array
}
What if instead of just printing, we want to double every element in the array and double it? Instead of rewriting the same function, we can generalize this:
function forEach(arr, method, i = 0) {
if (i == arr.length) return;
method(arr[i], i)
return forEach(arr, method, i + 1)
}
// notice that forEach looks exactly like printValues, except now, we can pass in anything we would have passed into a normal for loop
forEach([1,2,3,4,5], function(item, i) {
const x = i * 2
console.log(x);
})
// now back to OP's example, we can use this forEach function
forEach(arr, function (item, i) {
console.log(item)
})
Of course, Javascript has these functions built-in and they are also optimized. So, you can just use the built-in forEach:
arr.forEach(function(item, i) {
console.log(item)
// or console.log(arr[i]);
})
There are other functions to use too other than forEach, these include:
map
filter
reduce
There are more, but these are the fundamental building blocks for iteration on arrays in functional Javascript and are must-haves in your toolkit!
I think second snippet explains itself only the first one needs explanation.
First of all there is no sense to write i = arr[i] inside a loop. It changes the looping number of the element at corresponding index. Which means anything unexpected could happen
What happened in your case is below
In first iteration i is 0 then its changed to 3 due to i = arr[i].
In second iteration i is 4 because last value of i is incremented due to i++. Due to i = arr[i] i becomes 4 the arr[4]
In third iteration i becomes 5 due to increment i++ and due i = arr[i] it remains 5.
In fourth iteration it becomes 6 and loop ends because i < arr.length becomes false
const arr = [3, 'foo', { bar: 'baz' }, false, 4, 5];
for(var i = 0; i < arr.length; i++) {
console.log(`Initial i = ${i}`)
i = arr[i];
console.log(`Final i = ${i}`);
console.log('\n')
}
I'm trying to understand recursion and I have a somewhat decent understanding on how it intuitively works, however the aggregation of the returned data is the bit I struggle with.
For instance, in javascript to flatten an array I came up with the following code:
var _flatten = function(arr){
if(!arr instanceof Array) return arr;
var g = [];
function flatten(arr){
for(var i = 0; i < arr.length;i++){
if(arr[i] instanceof Array){
flatten(arr[i]);
}else{
g.push(arr[i]);
}
}
}
flatten(arr);
return g;
}
Turning something like this
var list = [1,2,3,4,5,6,[1,2,3,4,5,[1,2,3],[1,2,3,4]]];
into this:[ 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 1, 2, 3, 1, 2, 3, 4 ]
Which is fine and all, but the global variable g seems like some kind of cheap hack. I don't know how to think about the result returned when getting to the top of the stack and the return of the function propagating back down the stack. How would you implement this function, and how can I get a better grasp on this?
Thanks!
Instead of a global variable (to make it more proper recursion) you can send in g as a argument to the flatten function, and pass the modified g back out with a return statement.
var _flatten = function(arr) {
if (!arr instanceof Array) return arr;
function flatten(arr, g) {
for (var i = 0; i < arr.length; i++) {
if (arr[i] instanceof Array) {
flatten(arr[i], g);
} else {
g.push(arr[i]);
}
}
return g;
}
return flatten(arr, []);
}
There are many ways to write an array flattening procedure but I understand your question is about understanding recursion in general
The g isn't global in any sense of the word, but it is a symptom of the implementation choices. Mutation isn't necessarily bad so long as you keep it localized to your function – that g is never leaked outside of the function where someone could potentially observe the side effects.
Personally tho, I think it's better to break your problem into small generic procedures that make it much easier to describe your code.
You'll note that we don't have to setup temporary variables like g or handle incrementing array iterators like i – we don't even have to look at the .length property of the array. Not having to think about these things make it really nice to write our program in a declarative way.
// concatMap :: (a -> [b]) -> [a] -> [b]
const concatMap = f => xs => xs.map(f).reduce((x,y) => x.concat(y), [])
// id :: a -> a
const id = x => x
// flatten :: [[a]] -> [a]
const flatten = concatMap (id)
// isArray :: a -> Bool
const isArray = Array.isArray
// deepFlatten :: [[a]] -> [a]
const deepFlatten = concatMap (x => isArray(x) ? deepFlatten(x) : x)
// your sample data
let data = [0, [1, [2, [3, [4, 5], 6]]], [7, [8]], 9]
console.log(deepFlatten(data))
// [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
console.log(flatten(data))
// [ 0, 1, [ 2, [ 3, [ 4, 5 ], 6 ] ], 7, [ 8 ], 9 ]
First you'll see I made two different flattening procedures. flatten to flatten one level of nesting, and deepFlatten to flatten an arbitrarily deep array.
You'll also see I use Array.prototype.map and Array.prototype.reduce since these are provided by ECMAScript but that doesn't mean you're only limited to using procedures that you have. You can make your own procedures to fill the gaps. Here we made concatMap which is a useful generic provided by other languages such as Haskell.
Utilizing these generics, you can see that deepFlatten is an insanely simple procedure.
// deepFlatten :: [[a]] -> [a]
const deepFlatten = concatMap (x => isArray(x) ? deepFlatten(x) : x)
It's made up of a single expression including a lambda made up of a single if branch (by use of the ternary operator ?:)
Maybe it's a lot to take in, but hopefully it demonstrates that "writing a recursive procedure" isn't always about complicated setup of state variables and complex logic to control the recursion. In this case, it's a simple
if (condition) recurse else don't
If you have any questions, let me know. I'm happy to help you in any way I can.
In fact recursive coding is very simple and every aspects of it shall be contained in the function body. Any info that needs to pass shall be sent through arguments to the next recursion. Usage of global thingies is very ugly and shall be avoided. Accordingly i would simply do the in place array flattening job as follows;
var list = [1,2,3,4,5,6,[1,2,3,4,5,[1,2,3],[1,2,[9,8,7],3,4]]];
function flatArray(arr){
for (var i = 0, len = arr.length; i < len; i++)
Array.isArray(arr[i]) && (arr.splice(i,0,...flatArray(arr.splice(i,1)[0])), len = arr.length);
return arr;
}
console.log(flatArray(list));
Having learned some Javascript from CodeAcademy, I wanted to try some exercises to test my knowledge.
The exercise was this
Write a JavaScript function which will take an array of numbers stored and find the second lowest and second greatest numbers, respectively. - See more at: http://www.w3resource.com/javascript-exercises/javascript-functions-exercises.php#EDITOR
My function was this
function checker(array) {
narray = array.sort();
console.log(narray);
console.log(narray[1] + "," + narray[array.length - 2]);
}
checker([1, 2, 3, 4, 5]);
Their function was this
function Second_Greatest_Lowest(arr_num)
{
arr_num.sort(function(x,y)
{
return x-y;
});
var uniqa = [arr_num[0]];
var result = [];
for(var j=1; j<arr_num.length; j++)
{
if(arr_num[j-1] !== arr_num[j])
{
uniqa.push(arr_num[j]);
}
}
result.push(uniqa[1],uniqa[uniqa.length-2]);
return result.join(',');
}
alert(Second_Greatest_Lowest([1,2,3,4,5]));
Do you see any reason why the second option would be better?
As noted, their answer wants the 2nd unique number and it is rather inefficient as well.
There are two differences between your solution and theirs:
sorting function
uniqueness
The implicit sorting function is a natural sort. This means that the objects are converted to strings and compared as strings:
[1, 2, 3, 11].sort() // [1, 11, 2, 3]
[1, 2, 3, 11].sort(function(a,b){ return a-b }); // [1, 2, 3, 11]
The second difference, the uniqueness of the numbers. Your solution gives the number on the second position in the array, while theirs gives the number with the second lowest value:
[1,1,2] // Your solution: 1,1
[1,1,2] // Their solution: 2,1
While you can argue that it is not required, that is a matter of definition.
Either way, an improvement could be made in their solution as well to make it more efficient, though not as readable:
function getNumbers(arr) {
if (!arr.length) throw new Error('arr has no items');
arr = arr.slice().sort(function(a,b){ return a-b }); // copy and sort the array
for (var i=0; arr[i] === arr[0]; i++);
if (i === arr.length) { // all numbers are identical
return [arr[0], arr[0]];
}
var a = arr[i]; // second lowest number
i = arr.length-1;
var b = arr[i];
while (arr[i] === b) i--;
return [a, arr[i]];
}
// usage
getNumbers([2,3,1,1,1,6,4,5,6,1,11]) //[2, 6]
As you can see, once you found the number you're interested in, you no longer iterate through the sorted array. Compared to the solution that computes an array of unique numbers, this one is far more efficient, especially for large arrays.
No. Your function is just better. Really.
Reason is that on w3resource.com wants to show you how it should work - it's tutorial. You are just practical programmer.
Both solutions will work also when you put string inside, or negative numbers ..
The text of an exercise is misunderstood. They SHOULD have ask for a second "unique" number from the beginning and from the end. Then:
1, 1, 1, 1, 1, 2, 3, 4, 5, 6, 6, 6, 6, 6
Real answers are: 2, 5. Answers from Your script are 1 and 6.
So Your answer is OF COURSE great and OK, but their question is a bit inaccurate.
But a fact that You have note their mistake, and make a better algorithm, sugest that You can skip to another lesson :).
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 9 years ago.
Improve this question
Its kinda weird that the JavaScript Array class does not offer a last method to retrieve the last element of an array. I know the solution is simple (Ar[Ar.length-1] ), but, still, this is too frequently used.
Any serious reasons why this is not incorporated yet?
You can do something like this:
[10, 20, 30, 40].slice(-1)[0]
console.log([10, 20, 30, 40].slice(-1)[0])
The amount of helper methods that can be added to a language is infinite. I suppose they just haven't considered adding this one.
It's easy to define one yourself. That's the power of JavaScript.
if(!Array.prototype.last) {
Array.prototype.last = function() {
return this[this.length - 1];
}
}
var arr = [1, 2, 5];
arr.last(); // 5
However, this may cause problems with 3rd-party code which (incorrectly) uses for..in loops to iterate over arrays.
However, if you are not bound with browser support problems, then using the new ES5 syntax to define properties can solve that issue, by making the function non-enumerable, like so:
Object.defineProperty(Array.prototype, 'last', {
enumerable: false,
configurable: true,
get: function() {
return this[this.length - 1];
},
set: undefined
});
var arr = [1, 2, 5];
arr.last; // 5
Because Javascript changes very slowly. And that's because people upgrade browsers slowly.
Many Javascript libraries implement their own last() function. Use one!
i = [].concat(loves).pop(); //corn
icon cat loves popcorn
Another option, especially if you're already using UnderscoreJS, would be:
_.last([1, 2, 3, 4]); // Will return 4
Array.prototype.last = Array.prototype.last || function() {
var l = this.length;
return this[l-1];
}
x = [1,2];
alert( x.last() )
Came here looking for an answer to this question myself. The slice answer is probably best, but I went ahead and created a "last" function just to practice extending prototypes, so I thought I would go ahead and share it. It has the added benefit over some other ones of letting you optionally count backwards through the array, and pull out, say, the second to last or third to last item. If you don't specify a count it just defaults to 1 and pulls out the last item.
Array.prototype.last = Array.prototype.last || function(count) {
count = count || 1;
var length = this.length;
if (count <= length) {
return this[length - count];
} else {
return null;
}
};
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
arr.last(); // returns 9
arr.last(4); // returns 6
arr.last(9); // returns 1
arr.last(10); // returns null
Here is another simpler way to slice last elements
var tags = [1, 2, 3, "foo", "bar", "foobar", "barfoo"];
var lastObj = tags.slice(-1);
lastObj is now ["barfoo"].
Python does this the same way and when I tried using JS it worked out. I am guessing string manipulation in scripting languages work the same way.
Similarly, if you want the last two objects in a array,
var lastTwoObj = tags.slice(-2)
will give you ["foobar", "barfoo"] and so on.
pop() method will pop the last value out. But the problem is that you will lose the last value in the array
Yeah, or just:
var arr = [1, 2, 5];
arr.reverse()[0]
if you want the value, and not a new list.