JS - Loops and Higher Order function

loops are helpful when we want to execute the same code a lot of times.
for loop
Here below is a example of printing 1 to 10
for(let i = 1; i <= 5; i++){
console.log(i)
}
Output
1
2
3
4
5
Pointers:
let i = 1, simply initializing a variable called i to 1, this only happens once every loopi <= 5, condition checking if true it will execute the code inside the loop {}i++, simply incrementing I from 1 to 2, this happens after executing the code inside the loop(if condition matches) then we check the condition again if true then execute code again, then increment, .... as long as condition is true, finally i becomes 6 which is not less than or equal to 5 and we break out of the loop.If you make a loop where at no point the condition is false it creates a infinite loop, and will run infinitely.
Nested loops
printing the pattern
*
**
***
****
*****
Code:
for(let i=1; i<=5; i++){
for(let j=1; j<=i; j++){
process.stdout.write('*')
}
console.log()
}
Pointer:
Outer loop runs for 5 times and each time inner loop runs i number of times, first 1 then 2 then 3...hence it prints * in that order
process.stdout.write()only works on node not browser console, you can use it to print on console without sending the next output to next line.inner loop can access outer loops variable ( i ) due to scope but outer loop cannot access inner loops variables (variables created inside inner loop like j)
Printing Array elements
let superHeroes = ["wonder woman", "she hulk", "robin", "dare devil"]
for(let i=0; i<superHeroes.length; i++){
console.log(superHeroes[i])
}
Output
wonder woman
she hulk
robin
dare devil
Pointer : If you go out of max array length it won't throw error but return undefined elements.
break and continue
for(let i=1; i<=5; i++){
if(i === 3){
break;
}
console.log(i)
}
console.log('--OUT OF LOOP--')
Output
1
2
--OUT OF LOOP--
As soon as i === 3 condition was true break was triggered and we went out of the loop terminating it.
for(let i=1; i<=5; i++){
if(i === 3){
continue;
}
console.log(i)
}
console.log('--OUT OF LOOP--')
Output
1
2
4
5
--OUT OF LOOP--
continue simply doesn't execute the code in that iteration after it, goes to next iteration.
Note: In case of nested loops, break and continue work on the inner most loop only where it is written and doesn't affect the other ones.
while and do while
let's copy the same for loop we used in the first example of printing 1 to 5
for(let i = 1; i <= 5; i++){
console.log(i)
}
while loop is a different way of creating the loop, where only conditional part is necessary.
let i = 1
while(i <= 5){
console.log(i)
i = i+1
}
Now let's write the same logic in do while loop.
let i = 1
do {
console.log(i)
i = i+1
} while(i <= 5);
Pointers:
One advantage of for loop is i stays inside the loop's scope
control flow for for and while is the same declaration -> condition check -> execute code inside loop -> increment/decrement -> execute code ->.....until condition is false. do while is a bit different, for do while we have declaration -> execute code inside loop -> increment/decrement -> condition check -> execute code.......
Hence for i starting with 6 others will return nothing as it won't satisfy i <= 5 condition and we will never execute the code within loop, for do while we first print 6, then after when condition doesn't satisfy we go out of the loop.
High order Array & Object loops
Iterating Array with for of loop
const arr = [1, 2, 3, 4, 5]
for (const element of arr) {
console.log(element)
}
Output
1
2
3
4
5
Iterating String with for of loop
const word = 'hello'
for (const letter of word) {
console.log(letter)
}
Output
h
e
l
l
o
Iterating Map with for of loop
Maps are very similar to Objects, they have key value pairs as well but remembers order the keys were inserted, has unique keys (if you wanna insert duplicate keys it will silently fail)
Use [map name].set() method to insert key value pairs.
const ourMap = new Map()
ourMap.set('IN', 'India')
ourMap.set('USA', 'United States of America')
ourMap.set('Fr', 'France')
console.log(ourMap)
Output
Map(3) {
'IN' => 'India',
'USA' => 'United States of America',
'Fr' => 'France'
}
Now lets try and apply for of loop
for (const element of ourMap) {
console.log(element)
}
Output
[ 'IN', 'India' ]
[ 'USA', 'United States of America' ]
[ 'Fr', 'France' ]
So it returns each element of the map grouped as an array now to separate them we can use element[0] and element[1] or use a different syntax in the for of loop itself.
for (const [key, value] of ourMap) {
console.log(key,'is',value)
}
Output
IN is India
USA is United States of America
Fr is France
This is how we can restructure the array element.
Iterating Object with for of loop (doesn't work)
Yes for of loops don't work on Objects....
Iterating Object with for in loop
for in loop actually returns the key only.
const programmingLanguages = {
js: 'javascript',
cpp: 'c++',
py: 'Python',
swift: 'swift by apple'
}
for (const key in programmingLanguages) {
console.log(key, '->', programmingLanguages[key])
}
Output
js -> javascript
cpp -> c++
py -> Python
swift -> swift by apple
Iterating Array with for in loop
since for in loop only returns the key not the value, in arrays that's the index 0,1,2,3,4...
const coding = ['java', 'python', 'cpp', 'js']
for (const key in coding) {
process.stdout.write(coding[key]+' ')
}
console.log()
Output
java python cpp js
Iterating Map with for in loop (doesn't work)
Yup maps keys aren't iterable like this, won't throw an error but will show no output.
for each loop - Arrays
A function that accepts up to three arguments(item, index and array itself). forEach calls the callback function (no name) one time for each element in the array.
for each loop always return undefined.
Performs the specified action for each element in an array.
const coding = ['java', 'python', 'cpp', 'js']
coding.forEach(function (item){
console.log(item)
})
Output
java
python
cpp
js
we may also use arrow function.
const coding = ['java', 'python', 'cpp', 'js']
coding.forEach((item) => {
console.log(item)
})
We can also pass a function reference and define the function separately.
const coding = ['java', 'python', 'cpp', 'js']
coding.forEach(printEachElement)
function printEachElement(item){
console.log(item)
}
To understand Internal working of forEach, let's create a forEach function ourselves.
const coding = ['java', 'python', 'cpp', 'js']
ourForEach((item) => (console.log(item)))
function ourForEach(action){
for(let i=0;i<coding.length;i++){
action(coding[i])
}
}
As you can see we had to pass a function into the ourForEach function hence it's called higher order functions.
forEach() expects a synchronous function, it doesn't wait for promises. There is no way to stop or break a forEach() loop other than throwing an exception or error.
If you want to use early termination use for, for...of and for...in.
Traversing array of objects using for each
const myCoding = [
{
languageName: "javascript",
fileName: 'js'
},
{
languageName: "python",
fileName: 'py'
},
{
languageName: "java",
fileName: 'java'
},
]
myCoding.forEach((item) => {
console.log(`\({item.languageName} has a file name of \){item.fileName}`);
})
Output
javascript has a file name of js
python has a file name of py
java has a file name of java
filter
Unlike forEach() with filter() we can return a shallow copy (no reference) of a new array created with elements that got returned by the call back function. Always have a condition inside the filter callback function that returns true or false. If true that item is included in the new array that will be returned.
const myNums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
const newNums = myNums.filter((item)=>(item > 4)) // Implitcit here, if you use {} make sure to use return
console.log(newNums) // [ 5, 6, 7, 8, 9, 10 ]
We can filter out based on the above condition with the help of forEach as well, let's check out an example
const myNums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
const newNums = []
myNums.forEach((item) => {
if(item > 4){
newNums.push(item)
}
})
console.log(newNums) // [ 5, 6, 7, 8, 9, 10 ]
Let's see another example where a user wants to filter books on a website based on certain criteria
const books = [
{ title: 'Book One', genre: 'Fiction', publish: 1981, edition: 2004 },
{ title: 'Book Two', genre: 'Non-Fiction', publish: 1992, edition: 2008 },
{ title: 'Book Three', genre: 'History', publish: 1999, edition: 2007 },
{ title: 'Book Four', genre: 'Non-Fiction', publish: 1989, edition: 2010 },
{ title: 'Book Five', genre: 'Science', publish: 2009, edition: 2014 },
{ title: 'Book Six', genre: 'Fiction', publish: 1987, edition: 2010 },
{ title: 'Book Seven', genre: 'History', publish: 1986, edition: 1996 },
{ title: 'Book Eight', genre: 'Science', publish: 2011, edition: 2016 },
{ title: 'Book Nine', genre: 'Non-Fiction', publish: 1981, edition: 1989 },
];
// If user wants to filter out all books in history genere
let userbooks = books.filter((book)=>{
if(book.genre === 'History'){
return book
}
})
console.log(userbooks)
console.log('----------------')
// If user wants to filter out all books in Non-Fiction category that came after 1990
userbooks = books.filter((book) => (book.genre === 'Non-Fiction' && book.publish > 1990))
console.log(userbooks)
Output
[
{
title: 'Book Three',
genre: 'History',
publish: 1999,
edition: 2007
},
{
title: 'Book Seven',
genre: 'History',
publish: 1986,
edition: 1996
}
]
----------------
[
{
title: 'Book Two',
genre: 'Non-Fiction',
publish: 1992,
edition: 2008
}
]
Map and Chaining
Use .map() when you want to perform operations on array elements and return a new shallow copy array with modified items. Inside the function whatever you return replaces the original item in the array with itself.
const myNumbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
const newNums = myNumbers.map((num) => {
return num+10
})
console.log(newNums)
// [ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 ]
To understand maps better let's implement our own map function.
const myNumbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
const newNums = ourMap((item)=>(item+10))
console.log(newNums)
function ourMap(action){
const newNumbers = []
for(let i=0;i<myNumbers.length;i++){
newNumbers.push(action(myNumbers[i]))
}
return newNumbers
}
chaining
You can stack maps and filters on top of each other.
const myNumbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
const newNums = myNumbers
.map((num) => (num * 10))
.map((num) => (num+1))
.filter((num) => num >= 40)
console.log(newNums)
Output
[
41, 51, 61, 71,
81, 91, 101
]
reduce
reduce returns one value only after traversing all the array elements. The function inside reduce returns a value that is retained by the accumulator in the next iteration and ultimately the accumulator is returned when all iterations are over.
const myNums = [1, 2, 3]
const myTotal = myNums.reduce(function (accumulator, currentval){
return accumulator + currentval
}, 1)
console.log(myTotal) // 7
// 1 is the initial value of the accumulator
let's write the same solution using an arrow function
const myNums = [1, 2, 3]
const myTotal = myNums.reduce((accumulator, currentvalue) => (accumulator + currentvalue), 1)
console.log(myTotal) // 7
Another example
const order = [
{dish: 'Pasta Carbonara',price:14, spicy: false, qty:2},
{dish: 'Dragon Ramen', price:12, spicy: true, qty:1},
{dish: 'Caesar Salad', price: 11, spicy: false, qty:3},
{dish: 'Inferno Wings', price: 11, spicy: true, qty:2},
{dish: 'Truffle Risotto', price:18, spicy: false, qty:1},
]
const onlySpice = order.reduce((acc, item)=>{
const category = item.spicy ? 'spicy' : 'mild'
acc[category].push(item.dish)
return acc
},{spicy: [], mild: []})
console.log(onlySpice)
/*
{
spicy: [ 'Dragon Ramen', 'Inferno Wings' ], mild: [ 'Pasta Carbonara', 'Caesar Salad', 'Truffle Risotto' ]
}
*/




