Functional Programming

명령형 프로그래밍과 선언적 프로그래밍

명령형 프로그래밍은 코드로 원하는 결과를 달성해 나가는 과정에 관심을 두는 프로그래밍 스타일이다.

// 공백( )을 하이픈(-)으로 바꿔주는 코드
// 명령형 풀이
var string = 'Hello I am Sun Lee.'
var urlFriendly = ''

for (var i = 0; i < string.length; i++) {
  if (string[i] === ' ') {
    urlFriendly += '-'
  } else {
    urlFriendly += string[i]
  }
}

console.log(urlFriendly)

선언적 프로그래밍은 어떤 일이 발생해야 하는지 기술하고, 실제로 그 작업을 처리하는 방법은 추상화로 아랫단에 감춰진다.

// 위와같이, 공백( )을 하이픈(-)으로 바꿔주는 코드
// 선언적 풀이
const string = 'Hello I am Sun Lee.'
const urlFriendly = string.replace(/ /g, '-')

console.log(urlFriendly)

선언적 접근방식은 더 읽기 쉽고, 추론하기도 쉽다.

명령형 접근방식으로 DOM 을 구축하는 과정은 다음과 같다.

var target = document.getElementById('target')
var wrapper = document.createElement('div')
var headline = document.createElement('h1')

wrapper.id = 'welcome'
headline.innerText = 'Hello World'

wrapper.appendChild(headline)
target.appendChild(wrapper)

리엑트를 사용하여 선언적으로 DOM 을 구축하는 코드는 다음과 같다.

const { render } = ReactDOM

const Welcome = () => (
  <div id="welcome">
    <h1>Hello World</h1>
  </div>
)

render(<Welcome />, document.getElementById('target'))

join, filter

join 은 배열의 모든 원소를 인자로 받아 구분자로 연결한 문자열을 반환.

Array.filter 는 원본 배열로부터 새로운 배열을 만들어낸다.

const schools = ['Yorktown', 'Washington & Lee', 'Wakefield']

// 콤마(,)를 사용하여 문자열을 얻을 수 있음
console.log(schools.join(',')) // Yorktown,Washington & Lee,Wakefield

// Array.filter를 사용하여 'W'로 시작하는 학교만 가져올 수 있음
const wSchools = schools.filter(school => school[0] === 'W')
console.log(wSchools) // ["Washington & Lee", "Wakefield"]

// Array.filter를 사용하여 특정 함수를 제외할 수 있음
const cutSchool = (cut, list) => list.filter(school => school !== cut)
console.log(cutSchool('Washington & Lee', schools).join(' * ')) // Yorktown * Wakefield

console.log(schools.join('\n'))
// Yorktown
// Washington & Lee
// Wakefield

Array.map

Array.map 은 그 함수를 배열의 모든 원소에 적용해서 반환받은 값으로 이루어진 새 배열을 만듬.

const schools = ['Yorktown', 'Washington & Lee', 'Wakefield']

const highSchools = schools.map(school => `${school} High School`)
console.log(highSchools.join('\n'))
// Yorktown High School
// Washington & Lee High School
// Wakefield High School

console.log(schools.join('\n'))
// Yorktown
// Washington & Lee
// Wakefield
const schools = ['Yorktown', 'Washington & Lee', 'Wakefield']

const highSchools = schools.map(school => ({ name: school }))
console.log(highSchools)

// [
//   {name: 'Yorktown'},
//   {name: 'Washington & Lee'},
//   {name: 'Wakefield'}
// ]

TIP

if/else 문 대신 3 항 연산자(? :)를 사용하면 간결하게 코드를 작성할 수 있다.

const editName = (oldName, name, arr) =>
  arr.map(item => {
    if (item.name === oldName) {
      return {
        ...item,
        name
      }
    } else {
      return item
    }
  })

// or

const editName = (oldName, name, arr) =>
  arr.map(item => (item.name === oldName ? { ...item, name } : item))

Array.map 과 Object.keys 를 함께 사용하면 객체를 배열로 변화시킬 수 있다.

const schools = { Yorktown: 10, 'Washington & Lee': 2, Wakefield: 5 }

const schoolArray = Object.keys(schools).map(key => ({
  name: key,
  wins: schools[key]
}))

console.log(schoolArray)

// [
//   { name: 'Yorktown', wins: 10},
//   { name: 'Washington & Lee', wins: 2},
//   { name: 'Wakefield', wins: 5},
// ]

Array.reduce

Array.reduce 는 배열의 각 요소마다 누적 계산값과 함께 함수를 적용해 하나의 값으로 줄인다.

배열에서 최댓값을 찾아야 하는 경우

const ages = [21, 18, 42, 40, 64, 63, 34]

const maxAge = ages.reduce((max, age) => {
  console.log(`${age} > ${max} = ${age > max}`)
  if (age > max) {
    return age
  } else {
    return max
  }
}, 0)

console.log('maxAge', maxAge)

// 21 > 0 = true
// 4 18 > 21 = false
// 42 > 21 = true
// 40 > 42 = false
// 64 > 42 = true
// 63 > 64 = false
// 34 > 64 = false
// maxAge 64

위 함수는 다음과 같다

const ages = [21, 18, 42, 40, 64, 63, 34]

const max = ages.reduce((max, value) => ((value, max) ? value : max), 0)

배열을 객체로 변환해야 하는경우

const colors = [
  { id: '-exkare', title: '과격한 빨강', rating: 3 },
  { id: '-jbwsof', title: '큰 파랑', rating: 2 },
  { id: '-prigbj', title: '회색곰 회색', rating: 5 },
  { id: '-ryhbhsl', title: '바나나', rating: 1 }
]

const hashColors = colors.reduce((hash, { id, title, rating }) => {
  hash[id] = { title, rating }
  return hash
}, {})

console.log(hashColors)

// '-exkare' :{title: "과격한 빨강", rating: 3},
// '-jbwsof':{title: "큰 파랑", rating: 2},
// '-prigbj':{title: "회색곰 회색", rating: 5},
// '-ryhbhsl':{title: "바나나", rating: 1}

서로 다른 값이 한번씩만 들어있는 배열로 반환해야 하는경우

const colors = ['red', 'red', 'green', 'blue', 'green']

const distinctColors = colors.reduce(
  (distinct, color) =>
    distinct.indexOf(color) !== -1 ? distinct : [...distinct, color],
  []
)

console.log(distinctColors) // ["red", "green", "blue"]