Dear StackOverflowers…
I have a set of posts:
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']}
];
And an ever increasing set of utility functions:
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);
And I would like to massage the data into the format:
[
{ 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...
]
Using a point-free style, utilising just the reusable, compact utility functions.
I can get the unique tags from all the posts:
const tagsFor = comp(
unique,
flatten,
map(prop('tags'))
);
tagsFor(posts);
And I can work out how to achieve what I want using map & filter:
tagsFor(posts).map(function(tag) {
return {
title: tag,
posts: posts.filter(function(post) {
return post.tags.some(t => t === tag);
});
};
});
I just can’t seem to get my head around achieving this in a tacit manner.
Any pointers would be gratefully received...
So with many thanks to @naomik for the restructuring and @Berghi for leading me down the rabbit hole of combinatory logic this is what I came up with…
First off, tagsFor is collecting all the unique entries of some nested arrays into a single array, which sounds like generic functionality rather than something specific to any particular problem so I rewrote it to:
const collectUniq = (p) => comp( // is this what flatMap does?
uniq,
flatten,
map(prop(p))
);
So taking @naomik’s input we have:
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')
);
The problem for any tacit solution is that the data (posts) is buried in makeTag in map.
SKI calculus, and BCKW logic gives us a useful set of combinatory logic functions, which I’ll just leave here:
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 <*>
We can could alias these to id, comp, pure, flip etc.. but in this instance I don’t think it helps grok anything.
So, let’s dig out posts with B (compose):
const buildTags = comp(
B(map, makeTag)(posts),
collectUniq('tags')
);
And now we can see it is in the form of f(x)(g(x)) where: f = B(map, makeTag); g = collectUniq('tags’); and x = posts:
const buildTags = S(B(map)(makeTag))(collectUniq('tags'));
Now it’s tacit, declarative, and easy to grok (to my mind anyway)
Right, somebody get me a beer that took me 3 DAYS! (ouch)