39
loading...
This website collects cookies to deliver better user experience
Map<A, Map<B, T>>
// to
Map<B, Map<A, T>>
T
의 인스턴스를 다룰 일이 있었어요. 워낙 많다보니 퍼포먼스향상의 목적으로 어떤 기준으로 그룹화를 해야 했죠. 예를하나 들어보면 T
는 리소스 A
를 사용하는데, T
인스턴스들을 같은 리소스를 공유하는 것들끼리 그룹화를 함으로써 어떤 리소스 A
가 할당되었을때 그 리소스를 사용하는 모든 T
들을 한번에 처리할 수 있게끔 하는것이죠.A
와 B
두가지 기준이 있었어요. 그래서 다음과같은 자료구조를 만들게 되었어요.Map<A, Map<B, T[]>>
Map A (Map B [T])
Map<B, Map<A, T[]>>
로 표현이 되어야 한다는 점이었어요.// boring imperative code …
func invertMap(ABTs: Map<A, Map<B, T[]>>):
BATs = new Map<B, Map<A, T[]>>
for (a, BTs) of ABTs:
for (b, Ts) of BTs:
if BATs has no key b then
BATs[b] = new Map<A, T[]>
BATs[b][a] = Ts
else
if BATs[b] has no key a then
BATs[b][a] = Ts
else
BATs[b][a].add Ts
return BATs
Map<B, _>
이 Applicative 를 만족할 수 만 있다면 그냥 sequence
로 처리할 수 있을것만 같았어요. 왜냐면 Map은 이미 Traversable 을 만족하기 때문이었죠. 하지만 금방 Map 으로는 pure
(혹은 of
), 그리고 ap
(혹은 <*>
, liftA
) 을 구현이 불가능하다는 점을 깨달았죠. 간단히 생각해봐도 Identity 법칙을 만족하는 함수의 Map 이 존재할 리가 없잖아요?Map<B, Map<A, T>>
의 Monoidal 연산을 구현하고 있었죠.invertMap :: (Monoid t, Ord a, Ord b) => Map a (Map b t) -> Map b (Map a t)
invertMap = foldr (unionWith mappend) empty . mapWithKey (fmap . singleton)
export function invertMap<KA, KB, T>(
kaOrd: Eq<KA>,
kbOrd: Eq<KB>,
monT: Monoid<T>
) {
const monAT = map.getMonoid(kaOrd, monT);
const monBAT = map.getMonoid(kbOrd, monAT);
return (mm: Map<KA, Map<KB, T>>) =>
getFoldableWithIndex(kaOrd).foldMapWithIndex(monBAT)(mm, (a, bt) =>
pipe(
bt,
map.map((t) => new Map<KA, T>().set(a, t))
)
);
}