Clarification on iterator - function usage

Hello this is probably a dumb thing to ask but I just don’t understand one thing;

const animals = ['Hen', 'elephant', 'llama', 'leopard', 'ostrich', 'Whale', 'octopus', 'rabbit', 'lion', 'dog'];

// Create the secretMessage array below
const secretMessage = animals.map(takefirst=(animals) => {
console.log( animals[0])
}
)

The code above gives me the Hello world message.

I realize that .map iterator puts each element of the animals list and runs the following function that is written afterwards. What I do not understand is this:

animals[0] is actually ‘Hen’ and for me to access its first letter I need to do something like:
const var1 = animals[0];
const firstletter1 = var1[0];

Otherwise I do not get the letter, instead I get the whole word.

Perhaps the name of the function and the constant variable being the same confuses me but I’d greatly appreciate it if someone can enlighten me.

no, map will call the callback function for each item in the array. So the first time map calls your takefirst callback function, it will pass 'Hen' to the parameter (animals) of your callback function. Personally i would name the parameter animal:

const secretMessage = animals.map(takefirst=(animal) => {
console.log( animal[0])
}
)

which is a more accurate name.

5 Likes

Thank you very much, it was a silly question but I’m a weird guy who has to understand the logic. The moment I start to continue on tasks without grasping the actual theory behind something, I start to falter.

it was not. Someone else implemented an abstraction layer (the map method) here, which might take some time to grasp.

you understand now?

5 Likes

Yeah thanks. I was trying to have the code acquire the first animal so that I may acquire its first letter. Where as the .map iterator already had picked the first animal. I simply had to go from there. :slight_smile:

1 Like

map doesn’t acquire the first animal/element of the array, it goes through each element in the array (each animal)

5 Likes

I know, my bad horrible English. I was talking more like, the first element was already called. I didn’t have to try to call it and then work on it. The iterators take every element when used and apply the callback function you write afterwards to each one of them. We don’t need to summon the first element and then have it run the code, the iterator automatically does that.

At least that’s what I understood.

5 Likes

Implementing things is a great way to improve understanding or proving to yourself that your understanding works.

function map(f, arr) {
  const result = []
  for (let i = 0; i < arr.length; i++) {
    result.push(f(arr[i], i, arr))
  }
  return result
}

map((x) => x + 1, [1, 2, 3]) // [2, 3, 4]
5 Likes

So you created a function called map.
Map function takes in 2 arguments, f and arr.
Then you create a for loop.
From 0 to the length of arr. You will push an element into the arr list…

That’s where I am lost. I do not understand that line at all.
result.push(f(arr[i], i, arr))
what happens when you do result.push(1(arr[0] ?
Or even if f is not a number but a variable. Unless f is also a function.
Wow my mind is extremely confused :smiley:

3 Likes

I’ve read the documentations online but I failed to understand how .includes() precisely functions.
I’ll paste a code, perhaps you can tell me what happens from step to step? I’ll write the step to step version of how I perceive things to progress.

unnecessaryWords = ['Defeat', 'Victory', 'Rule', 'Conquest']
story = ['Through much struggle we have done the impossible and won the victory against our foes who'd dare destroy our lands in an attempt of conquest.', 'To rule this land, one must be noble and understanding, know no defeat not only in the battlefield but also the hearts of its subjects.']
//since I'm too lazy to split every single word, I'll use JS :slight_smile:
 
const storyWords = story.split(' ');

const betterWords = 
storyWords.filter(function(word) {
 return !unnecessaryWords.includes(word)
 });

I changed the assignment slightly, the story is a bit of gibberish I just made up to explain my situation. Alas so far I’ll tell you what I see occuring in the code above:

1- We have a list named story. We take the story list and split it via the iterator into words.
2- Now we put the storyWords variable in and put it through a filter. The filter runs a function with the argument word.
3- The function is to return !unnecessaryWords.includes(word)
4- This is where I do not understand things:

A) I get that this is an anonymous function and thus the name we write up next to filter as the function name is irrelevant. However I do not understand what that word argument is. How does the code supply the word argument to it?
B) Does the .filter() iterator pass each element of the storyWords list as the variable no matter what you name it?

so f is a function, see the argument:

(x) => x + 1

which you call for every element in the array, here:

f(arr[i], i, arr)

what confuses me, is that this function has 1 parameter, but is called with 3 arguments.

5 Likes

Yeah perhaps if we did it with actual data and go over what it does, it might help us understand it. I did not understand the third argument.

Ps: I’d truly appreciate assistance with the post I made above. In regards to the .includes() iterator.

1 Like

just use one argument then:

result.push(f(arr[i]))

same as for map? filter will call your function (your anonymous function) and supplies an argument for the word parameter.

The fact that you have to ask this question, means you did not grasp map (despite saying you did). If you did, you wouldn’t have to ask this question.

Have you studied ionatan implementation of the map function? It should clarify a lot of things

what does it reference? word? Then yes, you can name parameters anything you like, but its really preferred to stick to a logic parameter name.

5 Likes

I feel like a naughty child lol. I looked at the implementation, the third argument confused me. But I think I understand it now. With iterators you offer a function and an argument to the function.

The argument will be like a variable you can use at the function.
The iterator will input the element as the variable. Then run the function using said variable.

As in…

const betterWords = 
storyWords.filter(function(word) {
 return !unnecessaryWords.includes(word)
 });

Will run the filter iterator. Which runs through every single element assigning them to word.
Then inside that is the unnecessaryWords.includes iterator.
Includes again checks every element of the unnecessaryWords list, then corresponds them with the words argument from above aka the soryWords list’s elements. Now if there was no ! sign then it’d have only picked the ones that are the same, yet since we have opposite sign it only picks the ones that are not the same.

Did I get that right?

I wouldn’t call them iterators. I wrote one of them above, just a function. You’re giving a special name to something that isn’t special, making it mystical. That’s why implementing them is a good idea, there’s nothing mystical about what you’ve implemented because implementing something is to fully explain it.
They do represent iteration, yes, but iterator usually means something else.
Functions are basic units of abstraction. map/filter are implemented with functions. You should know what a function is. You should therefore also know all you need to know to fully understand map/filter. I suspect any confusion comes from some combination of not understanding functions, and attributing mystical features to map/filter. Learn the basics, drop the mystical attributes, and you’re good to go.

map f [1, 2, 3]
is equivalent to:
[f(1), f(2), f(3)]

Consider the basic mechanics of calling a function. When you call a function, the parameters get bound to the arguments. Nobody is touching or even looking at your variables, the only thing map and filter do with f is to call it (see my map implementation).

extra arguments to f

Just the value, the first argument, would be enough. Array.map does however send in another two arguments, I mimicked that. Javascript silently ignores additional arguments.
To use a function, you have to know what to give it and you have to know what to do with what it gives you back. Don’t know? Look it up. map/filter accept a function and an array. Additionally, they expect certain behaviour in terms of input and output from the function you provide it, for map, the function should accept one of the elements and return a replacement. For filter, the function should accept one of the elements and indicate whether or not it should be kept. They return new arrays, for map it is the replacements made by your function, for filter it is the original elements, but only those that your function indicated should be kept. The input/output boundaries are more important to pay attention to than whatever happens to be between them, it’s those boundaries that you’ll be interacting with.

I suggest implementing filter. It’s almost the same as map. You might start by filtering something with pen and paper. Write down some numbers, and a condition, then filter based on that condition, what do you get as the result? Your filter function should be doing the same thing. Any questions about filter should then arguably be answerable by looking at your own implementation of filter.

You’ll probably run into reduce soon if you haven’t already. map and filter look really basic, and they are, but reduce can seem completely magical. It doesn’t do much more than map/filter, it’s just yet another looping pattern abstracted out into a function (iterating with an accumulator, for example, a running sum)

// sum: at each iteration: add the value to the accumulator
reduce(add, [1, 2, 3]) // 6
// product: at each iteration: multiply the value to the accumulator
reduce(mul, [2, 3, 4]) // 24
6 Likes

Oh and those functions get misrepresented when using too much text.

map f:
  apply f to each value in the array, return the results in a new array

> map (+1) [1, 2, 3]
[2, 3, 4]


filter f:
  return a new array containing only the values for which f returns true

> filter (>3) [0..5]
[4, 5]
6 Likes

For starters thank you VERY much for the detailed explanation. A couple of hours ago I found out precisely what you wrote here. In my mind I thought there was a group of things called “iterators”. I’m not a Native English speaker so I didn’t know the true meaning of iterator. Alas I then made this weird conclusion that these special group of iterators(built-in functions in reality) were different than the rest, because unlike the rest of the functions and stuff these functions actually were applied to lists and when applied to lists they summoned and executed the following code on every single element of the array.

At any rate I found out couple hours ago that there is no such thing as iterator group. .map, .reduce, .filter are all built-in functions. They do not have a special name or a category however these few functions do indeed apply themselves to every single element of an array :smiley: After finding out about this the whole problem solved itself.

Lesson I got from this was to read the definition of everything properly and not half arsedly :smiley:
I’ll also do what you just suggested from now on. If something starts to confuse me, I’ll try to convert them into a function to understand what they do. Once I understand their function, I can also understand how to use them properly.
Thanks again!

1 Like

When I hear “iterator” I think of a value that represents an ongoing iteration.
As an example, this fits my idea of an iterator:

const range = (begin, end) => {
  return () => {
    if (begin === end) {
      return undefined
    }
    return begin++
  }
}

const iterator = range(3, 7)
console.log(iterator())
console.log(iterator())
console.log(iterator())
console.log(iterator())
console.log(iterator())
console.log(iterator())
console.log(iterator())
console.log(iterator())
console.log(iterator())

output:

3
4
5
6
undefined
undefined
undefined
undefined
undefined
2 Likes

This post is intended to make things confusing. And maybe a bit enlightening.
You can think of map like lifting a function into a higher dimension, or into some box.

If you do (+1) to 4, then you get 5
so (+1) operates on a 0-dimensional array (no array. just 1 value)

> inc0d = (+1)
> inc0d 4
5

using map, we can increase that to a 1-dimensional array.

> inc1d = map inc0d
> inc1d [1,2]
[2,3]

…can do this as many times as we want.

> inc2d = map inc1d
> inc2d [[1, 2], [3, 4]]
[[2,3],[4,5]]

What if you think of a function as … some kind of box. You could apply f to the result of the other function. You could map over another function.

> f = (+1)
> g = (*2)
> h = map f g
> h 3
7

Just like with arrays, you could do this repeatedly the same way to lift a function into increasingly deep boxes.

actual js (because the above is something else):

// map for functions. aka function composition
const map = (f, g) => (x) => f(g(x))

const f = (x) => x + 1
const g = (x) => x * 2
const h = map(f, g)
console.log(
  h(3)
)
2 Likes

Here’s digitsum implemented through mapping functions over functions.

const funcMap = (f) => (g) => (x) => f(g(x))
const arrMap = (f) => (arr) => arr.map(f)
const chToInt = (x) => parseInt(x)
const intToChars = (n) => ('' + n).split('')
const reduce = (f) => (arr) => arr.reduce(f)
const add = (a, b) => a + b
const sum = reduce(add)
// digits is chToInt mapped over intToChars
const digits = funcMap(arrMap(chToInt))(intToChars)
// digitSum is sum mapped over digits
const digitSum = funcMap(sum)(digits)
console.log(digitSum(1234))  // 10
console.log(digitSum(12345)) // 15

It’s a bit messy for sure, javascript’s syntax isn’t great for this (could be worse though, python would be worse)
Most of the functions there are totally trivial, they might not even seem very useful. But they become powerful when combined (map, reduce) with each other!

Same thing, in a language where this is all normal:

digitSum :: Int -> Int
digitSum = sum . (map digitToInt) . show

Perspective is important here. If you try to read it as a series of instructions you’ll have a very hard time. But if you consider functions to be implemented in terms of each other, then things may start to make some sense - using functions to compute functions. I computed the function digitSum from smaller functions.

2 Likes