일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- apollo client
- Ramda.js
- Front-End
- goorm.io
- 프로그래밍
- context api
- ECMAScript6
- 프론트엔드
- JavaScript
- VUE
- ELECTRON
- 코딩
- react.js
- schema-first
- 개발
- VanillaJS
- angular
- Programming
- 함수형
- 리액트
- 자바스크립트
- Design Pattern
- It
- Funtional programming
- 디자인패턴
- graphql
- vue.js
- Node.js
- code-first
- React
공부하는 블로그
Ramda.js를 이용한 함수형 프로그래밍 수련 본문
Ramda.js를 이용한 함수형 프로그래밍 수련
예전에 면접보러 다니면서 했던 개발 테스트 중 함수형 프로그래밍으로 풀어낼 수 있는 과제가 있어 다시 풀어보고자 합니다. JSON 파일은 여기 를 참조하면 되겠습니다.
1. 각 유저별로 전체 이벤트 갯수를 구하기 - TotalEvent는 모든 이벤트 숫자를 더한 값
결과는 아래와 같이 나오면 됩니다.
[
{
login: "rafaelfranca",
events: {
TotalEvent: 19
}
},
{
login: "HeshamAbdalla",
events: {
TotalEvent: 10
}
}
]
현재 json 배열 내의 값은 아래의 형태를 지닙니다.
{
"id": "8594176318",
"type": "IssueCommentEvent",
"actor": {
"id": 6443532,
"login": "bogdanvlviv"
}
}
type
내에 있는 값이 이벤트인것 같습니다. login
이라는 키 값에는 actor.login
값이 들어가면 되겠네요.
저는 Ramda.js 를 익힐 겸 Ramda를 활용했습니다. 비슷한 종류로는 lodash, underscore 같은 녀석들이 있습니다.
const events = require("./event.json");
const R = require("ramda");
R.pipe(
R.groupBy(item => item.actor.login),
R.toPairs,
R.map(([k, v]) => ({
login: k,
events: {
TotalEvent: v.length
}
})),
console.log
)(events);
선언된 순서대로 설명을 해보자면...
pipe
pipe는 함수형 언어의 주요 특징 중 하나인 함수 합성
을 하는 함수입니다. 비슷한 것으로는 R.compose
가 있는데, pipe는 왼쪽에서 오른쪽 순으로 합성을 한다면, compose는 오른쪽에서 왼쪽으로 합성을 합니다.
저는 pipe를 선호하는데, 아무래도 우리가 글을 읽을때도 왼쪽에서 오른쪽으로 읽고, 위에서 아래로 읽기 때문에 코드 가독성도 높아져서 pipe를 더 선호합니다.
compose의 경우에는 최종 결과가 어떤 형태인지 먼저 보고 싶을 때 활용하면 좋을 듯 합니다.
Ramda.js의 compose 예제를 살펴봅시다.
const classyGreeting = (firstName, lastName) => "The name's " + lastName + ", " + firstName + " " + lastName const yellGreeting = R.compose(R.toUpper, classyGreeting); yellGreeting('James', 'Bond'); //=> "THE NAME'S BOND, JAMES BOND"
R.compose
선언된 부분을 살펴보면R.toUpper
가 먼저 선언되어 있습니다. 이를 통해 "아 여긴 대문자가 나오겠구나"를 먼저 예상할 수 있습니다.
groupBy
groupBy를 이용해서 배열 내 객체의 이름별로 그룹화 할 수 있습니다. item => item.actor.login
이라는 선언을 통해서 오브젝트 내에 선언된 login 값을 이용해서 그룹화했습니다. 결과 값은 아래와 같습니다.
{
bogdanvlviv:
[ { id: '8594176318', type: 'IssueCommentEvent', actor: [Object] },
{ id: '8591675469', type: 'PullRequestEvent', actor: [Object] } ],
mostrozny:
[ { id: '8594070315', type: 'WatchEvent', actor: [Object] } ],
...
}
toPairs
Javascript 표준인 Object.entries 함수와 같은 기능입니다. 객체의 키, 값 쌍을 배열로 출력해 줍니다. toPairs 까지 합성한 결과는 아래와 같습니다.
[
[ 'bogdanvlviv', [ [Object], [Object] ] ],
[ 'mostrozny', [ [Object] ] ],
...
]
map
map을 통해서 이제 결과 도출을 해봅시다.
toPairs를 통해 얻어낸 key와 value의 길이를 이용해서 TotalEvents 값을 구할 수 있게 되었습니다.
[
{ login: 'bogdanvlviv',
events: { TotalEvent: 2 } },
{ login: 'mostrozny', events: { TotalEvent: 1 } },
...
]
2. 각 유저별로 각 이벤트 갯수
결과는 아래 형태와 같게 나오면 됩니다. 1번 문제의 확장 버젼이네요. 각 이벤트 별 발생 횟수를 추가하면 되겠습니다.
[
{
login: "rafaelfranca",
events: {
TotalEvent: 19,
IssueCommentEvent: 10,
PullRequestEvent: 2,
IssuesEvent: 2,
PushEvent: 1,
PullRequestReviewCommentEvent: 4
}
}
]
map
함수 내에서 R.countBy
를 이용하여 이벤트의 갯수를 쉽게 얻어냈습니다.
const events = require("./event.json");
const R = require("ramda");
R.pipe(
R.groupBy(item => item.actor.login),
R.toPairs,
R.map(([k, v]) => {
return {
login: k,
events: {
TotalEvent: v.length,
...R.countBy(e => e.type)(v) // 이 부분이 추가되었습니다.
}
};
}),
console.log
)(events);
3. 정렬
- 정렬 파라미터는 sort
- 주어진 이벤트가 많은 유저 순으로 정렬
- default값은 TotalEvent (전체 이벤트 수)
문제가 약간 애매하게 나왔는데, 전체 이벤트 수가 많은 순으로 정렬을 하되, 이벤트 종류가 많은 것부터 순서대로 정렬하라는 의미로 이해를 했습니다.
const events = require("./event.json");
const R = require("ramda");
R.pipe(
R.groupBy(item => item.actor.login),
R.toPairs,
R.map(([k, v]) => {
return {
login: k,
events: {
TotalEvent: v.length,
...R.countBy(e => e.type)(v)
}
};
}),
// 이 이하 부분이 추가되었습니다.
R.sortWith(
[
R.descend(
R.pipe(R.prop("events"), R.prop("TotalEvent"))
),
R.descend(
R.pipe(R.prop("events"), R.keys, R.length)
),
]
),
console.log
)(events);
sortWith
sortWith를 통해서 다양한 조건으로 정렬을 할 수 있습니다. R.descend(R.pipe(R.prop("events"), R.prop("TotalEvent")))
의 경우에는 events.TotalEvent
의 값을 내림차순으로 정렬한다는 의미입니다.
R.descend(R.pipe(R.prop("events"), R.keys, R.length))
의 경우에는 events의 종류 순으로 (keys.length
) 내림차순 정렬을 하라는 의미입니다.
두 조건을 배열에 묶어서 sortWith로 보내어 배열의 정렬을 해줍니다.
결론
이렇게 함으로서 간략하게 Ramda.js를 이용하여 다양한 함수의 합성과 정렬을 통해서 원하는 값을 도출해보았습니다. for, while, if 문 등을 통해서 코딩을 했더라면 이렇게 깔끔하게 코드를 짤 수 있었을까? 하는 생각이 듭니다. 이 글을 읽으시는 분들도 이 글을 통해서 함수형 프로그래밍의 매력에 빠져보시는 것은 어떨까 제안해봅니다. 질문이 있으시다면 아래에 댓글을 달아주세요! 감사합니다 😃
'자바스크립트' 카테고리의 다른 글
근황: 리액트 학습 (2) | 2021.08.14 |
---|---|
First look of Svelte App (0) | 2020.02.03 |
FP 비교 (2) | 2020.01.03 |
Vue.js Composition API 리뷰 (0) | 2019.12.21 |
Vue.js의 새롭게 개선된 prop.sync (0) | 2019.05.10 |