Удалить дубликаты в массиве Either с помощью fp-ts - PullRequest
0 голосов
/ 19 апреля 2020

Какой лучший способ удалить дубликаты массива Either в функциональном программировании с использованием fp-ts?

Это моя попытка:

import { either as E, pipeable as P } from "fp-ts";
import { flow } from "fp-ts/lib/function";

interface IItem {
  type: "VALID" | "INVALID";
  value: string;
}

// Building some fake data
const buildItem = (value?: string): E.Either<unknown, string> =>
  value != null ? E.right(value) : E.left({ type: "INVALID", value: "" });

// We will always have an array of Either
const items = [
  buildItem("aa"),
  buildItem("ab"),
  buildItem(),
  buildItem("ac"),
  buildItem("ab"),
  buildItem("ac"),
  buildItem(),
  buildItem("aa")
];

const checkList: string[] = [];
export const program = flow(
  () => items,
  x =>
    x.reduce(
      (acc, item) =>
        P.pipe(
          item,
          E.chain(value => {
            if (checkList.indexOf(value) < 0) {
              checkList.push(value);
              return E.right({ type: "VALID", value: value } as IItem);
            }
            return E.left({ type: "INVALID", value: value } as IItem);
          }),
          v => acc.concat(v)
        ),
      [] as E.Either<unknown, IItem>[]
    )
);

Ссылка на игровую площадку

1 Ответ

1 голос
/ 24 апреля 2020

Как правило, в fp-ts можно удалить дубликаты из Array<A>, используя uniq из fp-ts/lib/Array. С учетом Eq<A> и Array<A> он вернет Array<A>, где все A уникальны.

В вашем случае, похоже, вы хотите отменить дубликат Array<Either<IItem, IItem>>. Это означает, что для использования uniq вам понадобится экземпляр Eq<Either<IItem, IItem>>. То, как вы это получите, это использовать getEq из fp-ts/lib/Either. Требуется предоставить экземпляр Eq для каждого типа, параметризованного вашим Either, один для левого регистра, а другой для правого. Так что для Either<E, R>, getEq возьмет Eq<E> и Eq<R> и даст вам Eq<Either<E, R>>. В вашей ситуации E и R одинаковы (то есть IItem), поэтому вы просто дважды используете один и тот же экземпляр Eq<IItem>.

Скорее всего, вам нужен экземпляр Eq<IItem> будет выглядеть так:

// IItem.ts |
//-----------
import { Eq, contramap, getStructEq, eqString } from 'fp-ts/lib/Eq'

export interface IItem {
  type: "VALID" | "INVALID";
  value: string;
}

export const eqIItem: Eq<IItem> = getStructEq({
  type: contramap((t: "VALID" | "INVALID"): string => t)(eqString),
  value: eqString
})

Как только вы это сделаете, вы можете затем дедуплицировать ваш Array<Either<IItem, IItem>> с uniq следующим образом:

// elsewhere.ts |
//---------------
import { array, either } from 'fp-ts'
import { Either } from 'fp-ts/lib/Either'
import { IItem, eqIItem } from './IItem.ts'

const items: Array<Either<IItem, IItem>> = []

const uniqItems = uniq(either.getEq(eqIItem, eqIItem))(items)

Тогда константа uniqItems будет быть Array<Either<IItem, IItem>>, где никакие два Either<IItem, IItem> не равны, как определено Eq<Either<IItem, IItem>>.

...