I want to answer a small part of your question, that is nonetheless very interesting:
Is it possible to use a fold (also known as reduce) operation for this?
Yes, it is.
In fact, it is always possible to use a fold for anything! Fold is a general operation for iteration, anything you can do with iteration, you can do with fold!
The Wikipedia page for fold has a sketch of a proof for this, but I want to give another intuition thanks to a talk by Rúnar Óli Bjarnason on Functional Programming for Beginners which unfortunately seems to have vanished from the interwebs.
A collection can be either empty or not empty. The two arguments to fold tell it what to do when the collection is empty, and what to do with the next element when the collection is not empty. That covers all the cases, so it should be intuitively clear, that fold can do anything.
Another view, thanks to that same talk, is of fold as an interpreter. Think of a collection as a series of instructions. There are two kinds of instructions: the
HALT instruction (which has no operands), and the
NEXT instruction, which has one operand. The two arguments you supply to fold, are the two implementations of the interpreter functions for
NEXT. Again, since there are only two instructions, and you supply the code for both of them, it comes as no surprise, that fold can do anything.
I actually put this to the test, and re-implemented many of the methods from Ruby's collection framework (the
Enumerable module), which normally rely solely on the
each method (a
foreach-style iterator which simply yields each element), so that they instead all rely on the
inject method (which is how Ruby spells "fold").
So, what does it look like with a fold in ECMAScript?
someArray.reduce((memo, el, i, ary) => memo && ary === el, true)
However, just because fold can do everything, it shouldn't be used for everything. When there are more appropriate, more specialized operations, these should be used instead:
someArray.every((el, i, ary) => ary === el)
Note: technically speaking, these two implementations do not check whether every element is equal to every other element, they only check whether every element is equal to the first element. Thus, it relies on
=== being transitive and symmetric (as a well-behaved equality should be). If you cannot rely on that, you should first build the Cartesian Product of the array with itself, map it to whether the two elements are equal, and then ask if everything is true:
someArray.reduce((memo, el, _, ary) => memo.concat(ary.map(ell => [el, ell])), ).
map(([a, b]) => a === b).
every(el => el)