Как мне отобразить и отфильтровать это в бесточечном стиле

Уважаемые StackOverflowers…

У меня есть набор постов:

const posts = [
  { title: 'post1', tags: ['all', 'half', 'third', 'quarter', 'sixth']},
  { title: 'post2', tags: ['all', 'half', 'third', 'quarter', 'sixth']},
  { title: 'post3', tags: ['all', 'half', 'third', 'quarter']},
  { title: 'post4', tags: ['all', 'half', 'third']},
  { title: 'post5', tags: ['all', 'half']},
  { title: 'post6', tags: ['all', 'half']},
  { title: 'post7', tags: ['all']},
  { title: 'post8', tags: ['all']},
  { title: 'post9', tags: ['all']},
  { title: 'post10', tags: ['all']},
  { title: 'post11', tags: ['all']},
  { title: 'post12', tags: ['all']}
];

И постоянно увеличивающийся набор полезных функций:

const map = f => list => list.map(f);
const filter = f => list => list.filter(f);
const reduce = f => y => xs => xs.reduce((y,x)=> f(y)(x), y);
const pipe = (fn,...fns) => (...args) => fns.reduce( (acc, f) => f(acc), fn(...args));
const comp = (...fns) => pipe(...fns.reverse()); //  const comp = (f, g) => x => f(g(x));
const prop = prop => obj => obj[prop];
const propEq = v => p => obj => prop(p)(obj) === v;
const flatten = reduce(y=> x=> y.concat(Array.isArray(x) ? flatten (x) : x)) ([]);
const unique = list => list.filter((v, i, a) => a.indexOf(v) === i);
const add = a => b => a + b;
const addO = a => b => Object.assign(a, b);
const log = x => console.log(x);

И я хотел бы массировать данные в формате:

[
 { title: 'sixth', posts: [array of post objects that all have tag 'sixth'] },
 { title: 'quarter', posts: [array of post objects that all have tag 'quarter'] },
 { title: 'third', posts: [array of post objects that all have tag ’third'] },
 etc...
]

Используя бесточечный стиль, используя только повторно используемые компактные служебные функции.

Я могу получить уникальные теги из всех сообщений:

const tagsFor = comp(
  unique,
  flatten,
  map(prop('tags'))
);

tagsFor(posts);

И я могу решить, как добиться того, чего я хочу, используя карту и фильтр:

tagsFor(posts).map(function(tag) {
  return {
    title: tag,
    posts: posts.filter(function(post) {
      return post.tags.some(t => t === tag);
    });
  };
});

Я просто не могу понять, как достичь этого молчаливым образом.

Любые указатели будут с благодарностью получены...


person Adam    schedule 02.10.2016    source источник
comment
Просто абстрагируйте их в более полезные функции, и если они не кажутся достаточно общими, абстрагируйте их дальше. В дополнение к уже имеющемуся набору вам как минимум понадобятся функция равенства, функция создания объекта и функция удвоения параметра.   -  person Bergi    schedule 03.10.2016
comment
@Bergi спасибо за ваш вклад. Что такое функция удвоения параметра?   -  person Adam    schedule 03.10.2016
comment
Что-то вроде f => x = f(x)(x). Вместе с композицией функций и функцией flip вы можете использовать ее для создания параллельных потоков данных, например, она вам понадобится для tag (которая используется как для title, так и для posts). Или используйте более конкретный комбинатор функций, который выполняет нужную вам работу, иначе он станет действительно странным и непригодным для использования.   -  person Bergi    schedule 03.10.2016
comment
@Берги. Спасибо. Это должно быть f => x => f(x)(x)? Потратив день на обработку и настройку ввода @Naomik, я думаю, что «параллельные потоки данных — это то, к чему мне нужно стремиться. обновлю вопрос...   -  person Adam    schedule 04.10.2016
comment
Ах, конечно, я сделал опечатку со стрелкой.   -  person Bergi    schedule 04.10.2016
comment
@Bergi Берги, я хотел спросить — есть ли стандартное имя функции для функции удвоения параметра?   -  person Adam    schedule 04.10.2016
comment
Это функция join из Function Monad. Например, с bind = f => k => x => k(f(x))(x), но я не уверен, что общая терминология сильно поможет в этом конкретном случае использования. Вы также можете ознакомиться с более общим Стрелки с их более подходящим разветвлением, разветвлением, разветвлением.   -  person Bergi    schedule 04.10.2016
comment
@Bergi — Спасибо за подсказки. Знаете ли вы какие-либо примеры, когда эта функциональность использовалась в javascript?   -  person Adam    schedule 04.10.2016


Ответы (2)


Я вижу влияние некоторых других моих ответов на вашу текущую работу ^_^ @Bergi тоже дает вам хороший совет. Просто продолжайте создавать общие процедуры и составлять их вместе.

Я просто не могу понять, как достичь этого молчаливым образом.

Ну, цель не должна состоять в том, чтобы полностью отказаться от очков. Часто вы сталкиваетесь с действительно странными comp (comp (f)) и comp (f) (comp (g)) вещами, которые действительно трудно понять, когда вы вернетесь к ним позже.

Мы все еще можем сделать пару улучшений с вашим кодом, хотя

Это код, который мы меняем

// your original code
tagsFor(posts).map(function(tag) {
  return {
    title: tag,
    posts: posts.filter(function(post) {
      return post.tags.some(t => t === tag);
    });
  };
});

Это обновленный код

// yay
tagsFor(posts).map(makeTag(posts));

// OR
map (makeTag (posts)) (tagsFor (posts));

Вот утилиты

const comp = f => g => x => f (g (x));
const apply = f => x => f (x);
const eq = x => y => y === x;
const some = f => xs => xs.some(apply(f));
const filter = f => xs => xs.filter(apply(f));

const postHasTag = tag => comp (some (eq (tag))) (prop ('tags'));

const makeTag = posts => tag => ({
  title: tag,
  posts: filter (postHasTag (tag)) (posts)
});

Конечно, это всего лишь один из способов сделать это. Дайте мне знать, если это поможет или если у вас есть другие вопросы!


"Постоянно увеличивающийся набор полезных функций"

Наличие большого количества служебных функций может показаться ошеломляющим, но вам следует остерегаться некоторых из них, которые кажутся вам дублирующим поведением.

Возьмем, к примеру, этот...

const propEq = v => p => obj => prop(p)(obj) === v;

3 параметра не означают, что это плохая функция, но они должны, по крайней мере, заставить вас дважды подумать об этом и убедиться, что они необходимы. Помните, что становится все труднее составлять функции с большим количеством параметров, поэтому вам также следует тщательно продумать порядок параметров. В любом случае, эта функция propEq должна вас насторожить.

const eq = x => y => y === x;
const prop = x => y => y[x];
const propEq = p => x => comp (eq(x)) (prop(p))

Как только вы определили eq как функцию, вы должны быть в состоянии составить ее, когда столкнетесь с несоставляемым === в других ваших функциях. Это касается всех операторов в JavaScript.

В качестве небольшого задания взгляните на свои reduce, pipe и comp и попробуйте убрать пару баллов. Если вы застряли, дайте мне знать.

person Mulan    schedule 03.10.2016
comment
Большое спасибо (еще раз) за ваш подробный ответ. Это дало мне много поводов для размышлений и много игр. Ваше влияние на мое понимание действительно значительно. Вечером постараюсь обновить свой вопрос... - person Adam; 04.10.2016

Итак, большое спасибо @naomik за реструктуризацию и @Berghi за то, что привели меня в кроличью нору комбинаторной логики, вот что я придумал…

Во-первых, tagsFor собирает все уникальные записи некоторых вложенных массивов в один массив, что звучит как общая функциональность, а не что-то конкретное для какой-либо конкретной проблемы, поэтому я переписал его так:

const collectUniq = (p) => comp( // is this what flatMap does?
  uniq,
  flatten,
  map(prop(p))
);

Итак, принимая во внимание вклад @naomik, мы имеем:

const hasTag = tag => comp(  // somePropEq?
  some(eq(tag)),
  prop('tags')
);


const makeTag = files => tag => ({
  title: tag,
  posts: filter (hasTag(tag)) (files)
});


const buildTags = comp(
  map(makeTag(posts)),
  collectUniq('tags')
);

Проблема любого молчаливого решения заключается в том, что данные (посты) скрыты в makeTag на карте.

Исчисление SKI и логика BCKW дают нам полезный набор функций комбинаторной логики, которые я просто оставлю здесь:

const I = x => x;                       // id
const B = f => g => x => f(g(x));       // compose <$> 
const K = x => y => x;                  // pure
const C = f => x => y => f(y)(x);       // flip
const W = f => x => f(x)(x);            // join
const S = f => g => x => f(x)(g(x));    // sub <*>

Мы можем использовать псевдонимы для id, comp, pure, flip и т. д., но в данном случае я не думаю, что это поможет.

Итак, откапываем посты с B (compose):

const buildTags = comp(
  B(map, makeTag)(posts),
  collectUniq('tags')
);

И теперь мы можем видеть, что это в форме f(x)(g(x)), где: f = B(map, makeTag); g = collectUniq('метки'); и х = сообщения:

const buildTags = S(B(map)(makeTag))(collectUniq('tags'));

Теперь это неявно, декларативно и легко понять (во всяком случае, на мой взгляд)

Так, кто-нибудь, дайте мне пива, на что у меня ушло 3 ДНЯ! (ой)

person Adam    schedule 08.10.2016