oracle 같은 RDB 를 다루던 시절엔
user | dept
-----------
john | 0000
tom | 0001
jack | 0000
jane | 0001
과 같은 형태의 테이블일때 부서의 목록을 뽑아 내고자 group by 나 distinct 를 사용하여
dept
-----
0000
0001
이와 같이 뽑아내곤 했는데
noSQL 을 사용하거나 array collection 형태의 데이터를 다룰때에도 이런 작업을 해야할 경우가 있다.
일단 예제로 다뤄볼 대상 collection 의 구조를 가정해 보자.
documents = [
{
_id: "........",
message: "......",
user : {
_id: "........",
profile: {
displayName: "....."
}
}
]
흔한 게시판 형태의 데이터 구조라고 볼 수 있는데
각 documents 마다 고유한 _id 를 가지고 있고 해당 documents 를 작성한 user 객체가 각각 들어있는 경우라고 볼 수 있다.
그렇다면 documents 를 작성한 user 의 목록을 뽑아낼 수는 없을까?
node 콘솔이나 브라우저용 개발 툴에서 복사 붙이기 용으로 코드를 예제 코드를 한번 넣어보자면
documents = [ { _id: "doc0001", message: "document 01", user: { _id: "user0001", profile: { displayName: "john", } } }, { _id: "doc0002", message: "document 02", user: { _id: "user0002", profile: { displayName: "tom", } } }, { _id: "doc0003", message: "document 03", user: { _id: "user0001", profile: { displayName: "john", } } },
{ _id: "doc0004", message: "malformat" },
]
이런 식으로 넣었을때
[
, , , ]
요런 구성으로 들어갈 것이다.
마지막 doc0004는 user 가 없는데 당연히 저런 데이터가 있을 수 있다.
왜냐면 관계형 DB 처럼 고정 스키마가 없기 때문이다.
그러면 먼저 map 을 이용하여 user만 추출해보도록 하자.
documents.map(function(v) { return v.user && v.user.profile && v.user.profile.displayName ? v.user:null; });
[
, , , null]
마지막 user가 없는 경우는 조건식을 통해 null을 반환하도록 하였다.
그러면 이제 중복을 제거할 차례다.
중복을 제거하기 위해 reduce 를 사용하자.
reduce는 두개의 인자를 갖는 function을 사용하는데
document.reduce(function(a,b) {
return ...
});
이런 식으로 사용한다.
reduce 함수의 재미있는 점은 인자를 가져가는 방식이다
index | a | b
0 | array[0] | array[1]
1 | 0번째의 return 값 | array[2]
2 | 1번째의 return 값 | array[3]
......
n-1 | n-2번째의 return 값 | array[n]
------------------------------------
최종값 = n-1번째의 return 값
되시겠다.
이런 특성을 이용하여 중복이 발생하지 않는 user의 array를 만든다면 다음과 같은 전략을 짤 수 있다.
단순한 로직인데 a가 일단 array라는 가정이 필요하니 첫번째 시도때 a를 []로 초기화 해줘야하고 b가 a안에 있는지 여부를 검사하기 위해 some 이라는 function 을 사용하여 _id가 같은 것을 발견하면 true를 주게끔 해보자.
아, 그리고 null은 의도한 결과가 아니므로 당연히 예외처리.
어떠십니까? 엘레강스하십니까?
쓰고보니 재탕 느낌이 들어서 검색해보니 예전에 이런 글을 쓴적이 있긴 하네
http://spectrumdig.blogspot.kr/2012/03/javascript-union.html
그래도 map이랑 같이 썼으니 인정.
user | dept
-----------
john | 0000
tom | 0001
jack | 0000
jane | 0001
과 같은 형태의 테이블일때 부서의 목록을 뽑아 내고자 group by 나 distinct 를 사용하여
dept
-----
0000
0001
이와 같이 뽑아내곤 했는데
noSQL 을 사용하거나 array collection 형태의 데이터를 다룰때에도 이런 작업을 해야할 경우가 있다.
일단 예제로 다뤄볼 대상 collection 의 구조를 가정해 보자.
documents = [
{
_id: "........",
message: "......",
user : {
_id: "........",
profile: {
displayName: "....."
}
}
]
흔한 게시판 형태의 데이터 구조라고 볼 수 있는데
각 documents 마다 고유한 _id 를 가지고 있고 해당 documents 를 작성한 user 객체가 각각 들어있는 경우라고 볼 수 있다.
그렇다면 documents 를 작성한 user 의 목록을 뽑아낼 수는 없을까?
node 콘솔이나 브라우저용 개발 툴에서 복사 붙이기 용으로 코드를 예제 코드를 한번 넣어보자면
documents = [ { _id: "doc0001", message: "document 01", user: { _id: "user0001", profile: { displayName: "john", } } }, { _id: "doc0002", message: "document 02", user: { _id: "user0002", profile: { displayName: "tom", } } }, { _id: "doc0003", message: "document 03", user: { _id: "user0001", profile: { displayName: "john", } } },
{ _id: "doc0004", message: "malformat" },
]
이런 식으로 넣었을때
[
, , , ]
요런 구성으로 들어갈 것이다.
마지막 doc0004는 user 가 없는데 당연히 저런 데이터가 있을 수 있다.
왜냐면 관계형 DB 처럼 고정 스키마가 없기 때문이다.
그러면 먼저 map 을 이용하여 user만 추출해보도록 하자.
documents.map(function(v) { return v.user && v.user.profile && v.user.profile.displayName ? v.user:null; });
[
, , , null]
마지막 user가 없는 경우는 조건식을 통해 null을 반환하도록 하였다.
그러면 이제 중복을 제거할 차례다.
중복을 제거하기 위해 reduce 를 사용하자.
reduce는 두개의 인자를 갖는 function을 사용하는데
document.reduce(function(a,b) {
return ...
});
이런 식으로 사용한다.
reduce 함수의 재미있는 점은 인자를 가져가는 방식이다
index | a | b
0 | array[0] | array[1]
1 | 0번째의 return 값 | array[2]
2 | 1번째의 return 값 | array[3]
......
n-1 | n-2번째의 return 값 | array[n]
------------------------------------
최종값 = n-1번째의 return 값
되시겠다.
이런 특성을 이용하여 중복이 발생하지 않는 user의 array를 만든다면 다음과 같은 전략을 짤 수 있다.
- reduce function안에서 a,b 두개를 받는다.
- a 즉 지난번 reduce 실행값 중 b가 있는지 검사한다.
- 있으면 a 자신을 반환한다.
- 없으면 a에 b를 추가하고 a 자신을 반환한다.
단순한 로직인데 a가 일단 array라는 가정이 필요하니 첫번째 시도때 a를 []로 초기화 해줘야하고 b가 a안에 있는지 여부를 검사하기 위해 some 이라는 function 을 사용하여 _id가 같은 것을 발견하면 true를 주게끔 해보자.
아, 그리고 null은 의도한 결과가 아니므로 당연히 예외처리.
어떠십니까? 엘레강스하십니까?
쓰고보니 재탕 느낌이 들어서 검색해보니 예전에 이런 글을 쓴적이 있긴 하네
http://spectrumdig.blogspot.kr/2012/03/javascript-union.html
그래도 map이랑 같이 썼으니 인정.
Array::unique = ->
답글삭제output = {}
output[@[key]] = @[key] for key in [0...@length]
value for key, value of output
key,val 을 이용하여 해결하는 방법도 있음. some 을 사용하는 것보다 실행속도가 빠를 듯함.