Come with me now on a journey through code and data...

JavaScript Array Methods

Playing around with array methods in JavaScript

In [1]:
const exampleText = "A nut for a jar of tuna"
const textArray = exampleText.split(" ")

ForEach

As far as I understand it, forEach is just different syntax for a for loop. It doesn't return anything and is useful for things like logging to the console.

In [2]:
function toUpperCase(el, idx, arr) {
    return el.toUpperCase()
}
var upperCase = textArray.forEach(toUpperCase)
In [3]:
console.log(upperCase)
undefined
In [4]:
function printString(el, idx, arr) {
    console.log(`${idx} ${el}`)
}

textArray.forEach(printString)
0 A
1 nut
2 for
3 a
4 jar
5 of
6 tuna

Map

Map returns values - it maps arrays from some value to another value. As with all of these functions, it only applies to arrays and not objects.

In [5]:
var upperCase = textArray.map(toUpperCase)
In [6]:
console.log(upperCase)
[ 'A', 'NUT', 'FOR', 'A', 'JAR', 'OF', 'TUNA' ]

Cool.

Reduce

Reduce takes array items and groups them together back into a single variable.

In [7]:
function reverseString(accumulator, el, idx, arr) {
    return el + ' ' + accumulator
}
var reverseStr = textArray.reduce(reverseString, '')
In [8]:
console.log(reverseStr)
tuna of jar a for nut A 

Join

Since we're reducing to a String we could also be using join here:

In [9]:
textArray.join('')
Out[9]:
'Anutforajaroftuna'

Reverse

We can have the same effect as the reduce function by doing the following:

In [10]:
textArray.reverse().join(' ')
Out[10]:
'tuna of jar a for nut A'

You need to be careful as reverse is applied in place so affects all our code referring to textArray from now on.

In [11]:
textArray
Out[11]:
[ 'tuna', 'of', 'jar', 'a', 'for', 'nut', 'A' ]

Let's just put it back to how it was before

In [12]:
textArray.reverse()
Out[12]:
[ 'A', 'nut', 'for', 'a', 'jar', 'of', 'tuna' ]

Filter

In [13]:
function isThreeLetters(el, idx, arr) {
    return el.length === 3
}
var threeLetterWords = textArray.filter(isThreeLetters)
In [14]:
console.log(threeLetterWords)
[ 'nut', 'for', 'jar' ]

The above example is a more explicit description of whats going on, but it could be written in a more simplified way:

In [15]:
var threeLetterWords = textArray.filter(word => word.length === 3)

Concat

Pretty simple - concatenates arrays. Get's more interesting when dealing with nested arrays.

In [16]:
[1,2,3].concat(4)
Out[16]:
[ 1, 2, 3, 4 ]
In [17]:
[1,2,3].concat([4,5])
Out[17]:
[ 1, 2, 3, 4, 5 ]

Concatentation only pulls sub-elements out of an another array up to a depth of 1, otherwise it just concatenates the array itself.

In [18]:
[1,2,3].concat([[4], [[5, 6]]])
Out[18]:
[ 1, 2, 3, [ 4 ], [ [ 5, 6 ] ] ]

Reduce and Concat (Flat)

flat is currently in experimental stages and not supported by Node. The suggested replacement is a reduce followed by a concat. flat function logic is really useful for dealing with nested arrays, pulling out sub-elements and putting them all at the same depth.

First let's set up a shallow nested array:

In [19]:
function nestArrays(el) {
    return Array.from(el).map(toUpperCase)
}
var nestedChars = textArray.map(nestArrays)
In [20]:
console.log(nestedChars)
[ [ 'A' ],
  [ 'N', 'U', 'T' ],
  [ 'F', 'O', 'R' ],
  [ 'A' ],
  [ 'J', 'A', 'R' ],
  [ 'O', 'F' ],
  [ 'T', 'U', 'N', 'A' ] ]

Now write some code to flatten the nested array:

In [21]:
function concatenate(acc, el) {
    return acc.concat(el)
}
var unNestedChars = nestedChars.reduce(concatenate, [])

The above can be simplified to:

In [22]:
var unNestedChars = nestedChars.reduce((acc, el) => acc.concat(el), [])
In [23]:
console.log(unNestedChars)
[ 'A',
  'N',
  'U',
  'T',
  'F',
  'O',
  'R',
  'A',
  'J',
  'A',
  'R',
  'O',
  'F',
  'T',
  'U',
  'N',
  'A' ]
In [33]:
unNestedChars.reverse().join(' ')
Out[33]:
'A N U T F O R A J A R O F T U N A'

Surprise! Palindrome!

The above example works for a depth of 1. How about an even more nested array?

Now we create a deeply nested array:

In [25]:
function deepNest(acc, el) {
    return [acc, Array.of(el)]
}
var deepNestedChars = textArray.reduce(deepNest, [])
In [26]:
console.log(deepNestedChars)
[ [ [ [Array], [Array] ], [ 'of' ] ], [ 'tuna' ] ]

The problem can be solved neatly using recursion, introducing the Array.isArray function:

In [27]:
function flattenDeep(nestedChars) {
    return nestedChars.reduce((acc, el) => Array.isArray(el) ? acc.concat(flattenDeep(el)) : acc.concat(el), [])
}
In [28]:
console.log(flattenDeep(deepNestedChars))
[ 'A', 'nut', 'for', 'a', 'jar', 'of', 'tuna' ]

Reversing the Palindrome

Using what the above to try a few different methods of reversing the palindrome from the array of words:

In [29]:
textArray
Out[29]:
[ 'A', 'nut', 'for', 'a', 'jar', 'of', 'tuna' ]

The simplest way I can think of is joining it back into a string (without spaces) then making an array of chars and reversing:

In [40]:
Array.from(textArray.join('')).reverse().join('')
Out[40]:
'anutforajaroftunA'

More complicated is a map-reduce type example: reversing each word during a map step and then joining them back together again in reverse in a reduce step.

In [51]:
var reversedText = textArray.map(el => Array.from(el).reverse())
In [52]:
reversedText
Out[52]:
[ [ 'A' ],
  [ 't', 'u', 'n' ],
  [ 'r', 'o', 'f' ],
  [ 'a' ],
  [ 'r', 'a', 'j' ],
  [ 'f', 'o' ],
  [ 'a', 'n', 'u', 't' ] ]
In [54]:
reversedText.reduce((acc, el) => [el.concat(acc).join('')], [])
Out[54]:
[ 'anutforajaroftunA' ]

This is definitely awkward and overcomplicated in loads of ways but I just wanted to mess around with putting the logic purely inside map and reduce functions.