프로그래밍을 하다 보면, 두 배열 간의 연산이 필요한 경우가 많다. A배열에서 B배열에 있는 값을 제거한 C배열, A배열과 B배열에 같은 값만 추려낸 C배열, A배열과 B배열에 있는 값을 모두 갖고 있는 C배열.
차집합, 교집합, 합집합을 떠올렸다면 맞는 말이다. 지난 번에 소개한 lodash에 전부 가능하다.
차집합
1. _.difference
가장 간단한 함수이다. 두 배열 A와 B간의 차집합을 구한다.
const a = [1, 2, 3]
const b = [2, 4]
_.difference(a, b)
// [1, 3]
A배열에서 B배열의 차집합을 구하기 때문에 1, 3을 구할 수 있다. 해당 함수는 원시값은 가능 하지만 레퍼런스 형에서는 아래처럼 모두 다른 값으로 판단 되기 때문에 전부 리턴한다.
const a = [{ x: 1 }, { x: 2 }, { x: 3 }]
const b = [{ x: 2 }, { x: 4 }]
_.difference(a, b)
// [{ x: 1 }, { x: 2 }, { x: 3 }]
2. _.differenceBy
특정 속성의 값을 비교하거나, 특정 값으로 변경하여 비교해야 하는 경우 필요하다. 바로 위와 같은 경우이다.
바로 아래와 같이 x를 확인하게도 만들 수 있다.
const a = [{ x: 1 }, { x: 2 }, { x: 3 }]
const b = [{ x: 2 }, { x: 4 }]
_.differenceBy(a, b, 'x')
// [{ x: 1 }, { x: 3 }]
또한 아래와 같이 특정 속성값이 아니라, 특정 속성들을 가지고 비교해야 할 수 있다.
const a = [{ x: 1, y: 2 /* ,v: 2*/ }, { x: 1, y: 4 /* ,v: 4*/ }, { x: 2, y: 2 /* ,v: 4*/ }]
const b = [{ x: 2, y: 2 /* ,v: 4*/ }, { x: 4, y: 2 /* ,v: 8*/ }]
_.differenceBy(a, b, ({ x, y }) => x * y)
// [{ x: 1, y: 2 }]
위와 같이 하면 x와 y값의 곱을 가지고 계산 할 수 있다.
3. _.differenceWith
뭐 물론 위와 diffrentBy와 같이 특정 값을 만들어 내서 특정 값으로 차집합을 구할 수 있다. 하지만 x, y값 둘 다 같은 값을 가진 원소들을 제거하려면 어떻게 해야할까?
const a = [{ x: 1, y: 2 }, { x: 1, y: 4 }, { x: 2, y: 2 }]
const b = [{ x: 2, y: 2 }, { x: 4, y: 2 }]
_.differenceWith(a, b, (aElm, bElm) => aElm.x === bElm.x && aElm.y === bElm.y)
// [{ x: 1, y: 2 }, { x: 1, y: 4 }]
위와 같이 구할 수 있다. A집합의 각 원소들을 B집합의 각 배열의 원소들과 관계를 파악하고자 할 때 사용 가능하다.
교집합
1. _.intersection
두 배열 A와 B간의 교집합을 구한다.
const a = [1, 2, 3]
const b = [2, 4]
_.intersection(a, b)
// [2]
2. _.intersectionBy
당연하게도 intersectionBy도 있다.
const a = [{ x: 1 }, { x: 2 }, { x: 3 }]
const b = [{ x: 2 }, { x: 4 }]
_.intersectionBy(a, b, 'x')
// [{ x: 2 }]
또한 아래와 같이 특정 속성값이 아니라, 특정 속성들을 가지고 비교 할 수 있다.
const a = [{ x: 1, y: 2 /* ,v: 2*/ }, { x: 1, y: 4 /* ,v: 4*/ }, { x: 2, y: 2 /* ,v: 4*/ }]
const b = [{ x: 2, y: 2 /* ,v: 4*/ }, { x: 4, y: 2 /* ,v: 8*/ }]
_.intersectionBy(a, b, ({ x, y }) => x * y)
// [{ x: 1, y: 4 }]
위와 같이 하면 x와 y값의 곱을 가지고 계산 할 수 있다.
3. _.intersectionWith
const a = [{ x: 1, y: 2 /* ,v: 2*/ }, { x: 1, y: 4 /* ,v: 4*/ }, { x: 2, y: 2 /* ,v: 4*/ }]
const b = [{ x: 2, y: 2 /* ,v: 4*/ }, { x: 4, y: 2 /* ,v: 8*/ }]
_.intersectionWith(a, b, (aElm, bElm) => aElm.x === bElm.x && aElm.y === bElm.y)
// [{ x: 2, y: 2 }]
위와 같이 구할 수 있다. A집합의 각 원소들을 B집합의 각 배열의 원소들과 관계를 파악하고자 할 때 사용 가능하다.
특이사항을 한번 확인해보자.
왜 인자를 두개만 받을까? 두개만 받게되면 어떤역할을 할까? 실험해보자.
const a = [{ x: 1, y: 2 }, { x: 1, y: 4 }, { x: 2, y: 2 }]
const b = [{ x: 2, y: 2 }, { x: 4, y: 2 }]
_.intersectionWith([...a, ...b], (aElm, bElm) => aElm.x === bElm.x && aElm.y === bElm.y)
// [{ x: 1, y: 2 }, { x: 1, y: 4 }, { x: 2, y: 2 }, { x: 4, y: 2 }]
위 처럼 중복된 값이 제거 되었다. 위의 intersectionWith의 경우 반환 될 값을 다시 한번 검증을 하게 된다. 해당 값이 반환 될 값에 확정이 된 값과 같은 경우 반환하지 않도록 한다. 따라서 행위가 중복된 값을 제거하는 것처럼 보인다.
교집합
1. _.union
두 배열 A와 B간의 교집합을 구한다.
const a = [1, 2, 3]
const b = [2, 4]
_.union(a, b)
// [1, 2, 3, 4]
2. _.unionBy
당연하게도 unionBy도 있다.
const a = [{ x: 1 }, { x: 2 }, { x: 3 }]
const b = [{ x: 2 }, { x: 4 }]
_.unionBy(a, b, 'x')
// [{ x: 1 }, {x: 2}, {x: 3}, {x: 4}]
또한 아래와 같이 특정 속성값이 아니라, 특정 속성들을 가지고 비교 할 수 있다.
const a = [{ x: 1, y: 2 /* ,v: 2*/ }, { x: 1, y: 4 /* ,v: 4*/ }, { x: 2, y: 2 /* ,v: 4*/ }]
const b = [{ x: 2, y: 2 /* ,v: 4*/ }, { x: 4, y: 2 /* ,v: 8*/ }]
_.unionBy(a, b, ({ x, y }) => x * y)
// [{x: 1, y: 2}, { x: 1, y: 4 }, {x: 4, y: 2}]
위와 같이 하면 x와 y값의 곱을 가지고 계산 할 수 있다.
3. _.unionWith
const a = [{ x: 1, y: 2 /* ,v: 2*/ }, { x: 1, y: 4 /* ,v: 4*/ }, { x: 2, y: 2 /* ,v: 4*/ }]
const b = [{ x: 2, y: 2 /* ,v: 4*/ }, { x: 4, y: 2 /* ,v: 8*/ }]
_.unionWith(a, b, (aElm, bElm) => aElm.x === bElm.x && aElm.y === bElm.y)
// [{x: 1, y: 2}, {x: 1, y: 4}, { x: 2, y: 2 }, { x: 4, y: 2 }]
위와 같이 구할 수 있다. A집합의 각 원소들을 B집합의 각 배열의 원소들과 관계를 파악하고자 할 때 사용 가능하다.
특이사항
사실은 intersection, intersectionBy, union, unionBy, unionWith도 intersectionWith와 똑같이 하나의 배열을 받는 것이 가능하다. intersectionWith에서 설명한 것과 같이 중복된 값을 제거하는 것과 같은 효과를 지닌다.
댓글
댓글 쓰기