Skip to content

Angarsk8/funjs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

List Manipulation In JavaScript (Functional Approach)

This is a little project that I wrote in a couple of hours as a proof of concept. This project pretends to prove that is possible to recreate some of the most important methods in the Array.prototype object by just using Functional Programming concepts such as:

  • Pure Functions
  • Recursion
  • The Head and Tail Pattern
  • Immutability
  • Accumulators
  • Pattern Matching (Array Destructuring was the closest thing to Pattern Matching in JavaScript).

Constraints

I did not use any of the following built in features of JavaScript:

  • Control Flow Constructs (if..elseif..else, switch, for, while, do..while, try..catch..final, etc)
  • Mutable Variables (var, let)
  • Object Oriented Programming Techniques (classes, prototypes, plain objects, etc)
  • Array.prototype Methods (Obviously! With the exception of Array.prototype.isArray)

Functions

These are the functions that I decided to implement for this exercise:

Head

Returns the head or first element of the given list. Not available in Array.prototype.

export const hd = ([head, ..._]) => head

Tail

Returns the tail of the given list. Not available in Array.prototype.

export const tl = ([_, ...tail]) => tail

Length

Returns the length of the given list.

export const length = list => _length(list, 0)
const _length = ([_, ...tail], acc) =>
  ![_, ...tail][0] ? acc : _length(tail, acc + 1)

Is_List

I know, this is a very dumb function ¯\_(ツ)_/¯

export const is_list = list => Array.isArray(list)

Reduce

export const reduce = ([head, ...tail], acc = 0, fun) =>
  !length([head, ...tail]) ? acc : reduce(tail, fun(acc, head), fun)

Map

// Using our custom reduce function
export const map = (list, fun) =>
  reduce(list, [], (acc, val) => [...acc, fun(val)])

// Using a helper function, recursion and an accumulator
export const map2 = (list, fun) => _map2(list, fun, [])
const _map2 = ([head, ...tail], fun, acc) =>
  !length([head, ...tail]) ? acc : _map2(tail, fun, [...acc, fun(head)])

// Another solution
export const map3 = ([head, ...tail], fun) =>
  !length([head, ...tail]) ? [] : [fun(head), ...map3(tail, fun)]

Filter

// Using our custom reduce function
export const filter = (list, fun) =>
  reduce(list, [], (acc, val) => fun(hd(list)) ? [...acc, hd(list)] : acc)

// Using a helper function, recursion and an accumulator
export const filter2 = (list, fun) => _filter2(list, fun, [])
const _filter2 = ([head, ...tail], fun, acc) =>
  !length([head, ...tail]) ? acc : _filter2(tail, fun, fun(head) ? [...acc, head] : acc)

// Another solution
export const filter3 = ([head, ...tail], fun) =>
  !length([head, ...tail]) ? [] : (
    fun(head) ? [head, ...filter3(tail, fun)] : filter3(tail, fun)
  )

Foreach

export const foreach = ([head, ...tail], fun) =>
  !length([head, ...tail]) ? undefined : (
    fun(head) ? undefined : foreach(tail, fun)
  )

Reverse

export const reverse = ([head, ...tail]) =>
  !length([head, ...tail]) ? [] : [...reverse(tail), head]

Some

export const some = ([head, ...tail], fun) =>
  !length([head, ...tail]) ? false : (
    fun(head) ? true : some(tail, fun)
  )

Every

export const every = ([head, ...tail], fun) =>
  !length([head, ...tail]) ? true : (
    fun(head) ? every(tail, fun) : false
  )

Flatten

export const flatten = ([head, ...tail]) =>
  !length([head, ...tail]) ? [] : (
    is_list(head)
    ? [...flatten(head), ...flatten(tail)]
    : [head, ...flatten(tail)]
  )

Concat

export const concat = (list, ...values) => [...list, ...flatten(values)]

Fill

export const fill = (list, val, start = 0, end = length(list)) =>
  _fill(list, val, start, end, 0)

const _fill = ([head, ...tail], val, start, end, index) =>
  !length([head, ...tail]) ? [] : (
    index >= start && index <= end
    ? [val , ..._fill(tail, val, start, end, index + 1)]
    : [head, ..._fill(tail, val, start, end - 1, index + 1)]
  )

Join

export const join = (list, sep) =>
  reduce(list, "", (acc, val) => !acc ? `${val}` : `${acc}${sep}${val}`)

To_String

export const to_string = list => join(list, ",")

Find

export const find = ([head, ...tail], fun) =>
  !length([head, ...tail]) ? undefined : (
    fun(head) ? head : find(tail, fun)
  )

Range

Not available in Array.prototype, but still very useful to create lists of consecutive numbers.

export const range = (start, finish) => _range(start, finish, [])
const _range = (start, finish, acc) =>
  start > finish ? acc : _range(start + 1, finish, [...acc, start])

Releases

No releases published

Packages

No packages published