I saw this question and wondered how it'd be best to accomplish this using Ramda.
I'm new to the library so I thought it'd be an interesting learning exercise.
I have a working solution but it seems overly complicated and verbose. I'd be grateful for any suggestions on what other ways it would be possible to achieve this output using Ramda.
I'm aware that this doesn't need a library; as I say, I approached it as a learning exercise.
Here's what I have:
const { addIndex, always, fromPairs, join, map, sort, toPairs, unnest } = R;
const song = {
"99": [0, 7],
"bottles": [1, 8],
"of": [2, 9],
"beer": [3, 10],
"on": [4],
"the": [5],
"wall": [6]
};
const toSentence = join(' ');
const parsed = toSentence(
map(
([_, word]) => word,
sort(
([a, _], [b, __]) => a < b ? -1 : 1,
unnest(
map(
([word, indices]) => map(
index => [index, word],
indices,
),
toPairs(song)
)
)
)
)
)
console.dir(parsed)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
2 Answers 2
This is a great question.
I found no easy Ramda functional way to solve this, but you could just use a forEach approach. I find it easier to read.
const {forEachObjIndexed, forEach} = R;
const song = {
"99": [0, 7],
"bottles": [1, 8],
"of": [2, 9],
"beer": [3, 10],
"on": [4],
"the": [5],
"wall": [6]
};
let words = [];
forEachObjIndexed((indexes, word) => forEach(idx => words[idx] = word, indexes), song);
console.log(words.join(' '));
-
\$\begingroup\$ This is an interesting approach to the problem and I also find it easier to read than the code I had \$\endgroup\$OliverRadini– OliverRadini2019年04月03日 08:00:52 +00:00Commented Apr 3, 2019 at 8:00
Names
Variable names are very important,Try to keep the code understandable by defining named functions that relate to the abstract thing the do. (see example)
Same with the arguments. The names indices
, and index
are misleading. You are referring to the word position. Thus the names positions
and position
would be better suited. I would use pos
rather than position
Redundant code
In the sorting compare function the two arguments
_
, and__
are not not needed. The return can be any number value -10 and -1 are equivalent, so use the sorter fora-b
.([a, _], [b, __]) => a < b ? -1 : 1,
becomes([a], [b]) => a - b,
The function extracting the word can be
pair => pair[1],
or([, word]) => word,
rather than([_, word]) => word,
Why store the result in
parsed
. Just pass it on to the handling function. In this case the console.
Scope clutter
Keep the Ramba functions associated with the object so you don't end up with name clashes, and it makes it clearer what is doing what. (see example)
Example
const lyrics = {"99": [0, 7],"bottles": [1, 8], "of": [2, 9], "beer": [3, 10], "on": [4], "the": [5], "wall": [6]};
const log = console.log;
const song = R.join(' ');
const position = ([a], [b]) => a - b;
const word = ([,word]) => word;
const swapPair = ([word, positions]) => R.map(pos=> [pos, word], positions);
log(
song(
R.map(
word, R.sort(
position, R.unnest(R.map(
swapPair, R.toPairs(lyrics)
))
)
)
)
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
sort
and themap
, then the sort doesn't need to do \$n \cdot log(n)\$ array dereferences. Your sort function is unstable and won't preserve the ordering of equal elements. It doesn't matter in this problem; when it does matter, it's a source of nasty, subtle bugs, so always be aware of when you're doing that. \$\endgroup\$