I would not worry too much about finding it difficult at first. That’s the sign that you are delving into a whole different way to think, and that’s exactly the kind of stuff that levels you up, as it gives you the power to think about the same problem in different ways.
Learning something radically different is not just about absorbing information. It is about developing knowledge, and takes exercise and hands-on experience. Like learning to play an instrument or speak a language: reading about it is not enough, you have to train, spend time on it, and at the beginning it is clumsy and not pleasant, but each day you get better, until you master it and take pleasure in it.
You are probably familiar with other styles of programming, and each of them represents a way to look at computation. Imperative programming is about giving sequential instructions, like a receipt: do this, then do that, then if X do Y. Object oriented programming focuses on “objects” as the subject of our narrative: object X can do this and that, and changes its own state. I actually remember, back in studies, struggling a LOT with understanding what an object even is… reading over and over stuff like “objects group together state and behavior”, understanding the words without actually seeing the meaning of the sentence.
Functional programming is centered on data, and how to transform it. I know that this sentence will clarify exactly nothing, until after enough exercise it will just click. The thing is, we need to unlearn a lot before we can rewire into a different paradigm. Encapsulation, mutability, inheritance, etc. are all concepts that we need to abandon for a bit, and trust that we will come out with newfound knowledge.
A small practical example of centering our thought process on data, starting from your question:
It is basically the same, but it depends on what you center your narrative on: with imperative iteration you think mainly about what action you want to perform, in functional transformations you think about the data you have and the data you want.
In practice, when the data you start with is a collection, you can ask yourself what kind of data should the result be, and choose accordingly:
Another collection of the same size, but with different element? Use
map. Map goes to a collection of N elements to another collection of N different elements, obtained by transforming the original ones one by one.
The same collection, with some elements excluded? Use
filter. Filter goes from a collection of N elements to a collection of M < N of the same elements, excluding some of them.
Something different, like an aggregate result? Use
reduce. Reduce goes to a collection to… whatever you want In fact,
filter, etc. could be implemented with
In all these cases, the subject you are dealing with is your data. Data goes through functions, which return other new data (without changing the original).
In any case, the difficulty you are facing is not specific to you. I personally can relate fully to that, in my case it happened when I learned LISP coming from OOP. It wasn’t easy, but it was worth. Over time, it just started feeling natural, and I feel it expanded my mind right because it was so foreign to me at the start. And what’s great is that being able to think in different ways made me a better software engineer even when using OOP languages.