So... I have decided to define every array methods using recursion. (I haven't really tested all of them... so there might be some errors.)

Also, I only defined the "essence" of most methods. Didn't follow the complete spec for most.

# Why?

Why not?

# How is this useful?

It is not.

#
`Array.from`

`Array.from`

takes in two kinds of objects.

- Array-like objects that have a
`length`

property with zero-indexed elements - Iterable objects that have an iterator at
`[Symbol.iterator]`

```
const arrayFrom = (o) => {
if ('length' in o) return arrayFromArrayLike(o)
if (Symbol.iterator in o) return arrayFromIterator(o[Symbol.iterator]())
return []
}
const arrayFromArrayLike = (arrayLikeObject) => {
if (arrayLikeObject.length <= 0) return []
return [
...arrayFromArrayLike({
...arrayLikeObject,
length: arrayLikeObject.length - 1,
}),
arrayLikeObject[arrayLikeObject.length - 1],
]
}
const arrayFromIterator = (iterator) => {
const { value, done } = iterator.next()
if (done) return []
return [value, ...arrayFromIterator(iterator)]
}
```

Note: we ignore the 2nd and 3rd arguments of `Array.from`

. (see docs)

#
`Array.of`

```
const arrayOf = (...xs) => {
if (xs.length <= 0) return []
const [head, ...tail] = xs
return [head, ...arrayOf(...tail)]
}
```

#
`Array.prototype.concat`

```
const concat = (xs, ...arrays) => {
if (arrays.length <= 0) return xs
const [ys, ...restArrays] = arrays
if (ys.length <= 0) return concat(xs, ...restArrays)
const [head, ...tail] = ys
return concat([...xs, head], tail, ...restArrays)
}
```

Note: assuming concat only takes in 2 parameters

#
`Array.prototype.entries`

```
function* entries(xs, i = 0) {
if (xs.length <= 0) return
const [head, ...tail] = xs
yield [i, head]
yield* entries(tail, i + 1)
}
```

note: `i`

does not exist in `Array.prototype.entries`

#
`Array.prototype.every`

```
const every = (xs, predicate) => {
if (xs.length <= 0) return true
const [head, ...tail] = xs
return predicate(head) && every(tail, predicate)
}
```

#
`Array.prototype.fill`

```
const fill = (xs, k, start = 0, end = xs.length + 1) => {
if (xs.length <= 0) return []
const [head, ...tail] = xs
if (start > 0) return [head, ...fill(tail, k, start - 1, end - 1)]
return fillFromStart([head, ...tail], k, end)
}
const fillFromStart = (xs, k, end = xs.length + 1) => {
if (xs.length <= 0) return []
if (end <= 0) return xs
const [_, ...tail] = xs
return [k, ...fillFromStart(tail, k, end - 1)]
}
```

#
`Array.prototype.filter`

```
const filter = (xs, predicate) => {
if (xs.length <= 0) return []
const [head, ...tail] = xs
return [
...(predicate(head) ? [head] : []),
...filter(tail, predicate)
]
}
```

#
`Array.prototype.find`

```
const find = (xs, predicate) => {
if (xs.length <= 0) return undefined
const [head, ...tail] = xs
if (predicate(head)) return head
return find(tail, predicate)
}
```

#
`Array.prototype.findIndex`

```
const findIndex - (xs, predicate) => {
if (xs.length <= 0) return -1
const [head, ...tail] = xs
if (predicate(head)) return 0
return findIndex(tail, predicate) + 1
}
```

#
`Array.prototype.forEach`

```
const forEach = (xs, fn) => {
if (xs.length <= 0) return
const [head, ...tail] = xs
fn(head)
forEach(tail, fn)
}
```

notes: ignoring index

#
`Array.prototype.includes`

```
const includes = (xs, predicate) => {
if (xs.length <= 0) return false
const [head, ...tail] = xs
const predicate(head) || includes(tail, predicate)
}
```

#
`Array.prototype.indexOf`

```
const indexOf = (xs, x) => {
if (xs.length <= 0) return -1
const [head, ...tail] = xs
if (head === x) return 0
return indexOf(tail, x) + 1
}
```

#
`Array.prototype.join`

```
const join = (xs, separator = ',') => {
if (xs.length <= 0) return ''
const [head, ...tail] = xs
return `${head}${separator}${join(tail, separator)}`
}
```

#
`Array.prototype.map`

```
const map = (xs, fn) => {
if (xs.length <= 0) return []
const [head, ...tail] = xs
return [fn(head), ...map(tail, fn)]
}
```

#
`Array.prototype.reduce`

```
const reduce = (xs, fn, acc) => {
if (xs.length <= 0) {
if (typeof acc === 'undefined') {
throw new TypeError('Reduce of empty array with no initial value')
} else {
return acc
}
}
const [head, ...tail] = xs
if (typeof acc === 'undefined') return reduce(tail, fn, head)
return reduce(tail, fn, fn(acc, head))
}
```

#
`Array.prototype.reverse`

```
const reverse = (xs) => {
if (xs.length <= 0) return []
const [head, ...tail] = xs
return [...reverse(xs), head]
}
```

#
`Array.prototype.slice`

Slice is a surprisingly annoying one to define. It needs to deal with negative indices, but you can't simply "mod" the numbers...

```
const slice = (xs, start = 0, end = xs.length) => {
if (xs.length <= 0) return []
if (start < 0) return slice(xs, Math.max(0, start + xs.length), end)
if (end < 0) return slice(xs, start, Math.max(0, end + xs.length))
const [head, ...tail] = xs
if (end <= start) return []
if (start > 0) return slice(tail, start - 1, end - 1)
return [head, ...slice(tail, 0, end - 1)]
}
```

#
`Array.prototype.some`

```
const some = (xs, predicate) => {
if (xs.length <= 0) return false
const [head, ...tail] = xs
return predicate(head) || some(tail, predicate)
}
```

## Discussion (1)

You got me at

Here is my proposal for

`Array.prototype.flat`

.