I have following input string
Lorem ipsum dolor sit amet consectetur adipiscing elit sed doeiusmod tempor incididunt ut Duis aute irure dolor in reprehenderit in esse cillum dolor eu fugia ...
Splitting rules by example
[
"Lorem ipsum dolor", // A: Tree words <6 letters
"sit amet", // B: Two words <6 letters if next word >6 letters
"consectetur", // C: One word >=6 letters if next word >=6 letters
"adipiscing elit", // D: Two words: first >=6, second <6 letters
"sed doeiusmod", // E: Two words: firs<6, second >=6 letters
"tempor" // rule C
"incididunt ut" // rule D
"Duis aute irure" // rule A
"dolor in" // rule B
"reprehenderit in" // rule D
"esse cillum" // rule E
"dolor eu fugia" // rule D
...
]
So as you can see string in array can have min one and max tree words. I try to do it as follows but doesn't work - how to do it?
let s="Lorem ipsum dolor sit amet consectetur adipiscing elit sed doeiusmod tempor incididunt ut Duis aute irure dolor in reprehenderit in esse cillum dolor eu fugia";
let a=[""];
s.split(' ').map(w=> {
let line=a[a.length-1];
let n= line=="" ? 0 : line.match(/ /g).length // num of words in line
if(n<3) line+=w+' ';
n++;
if(n>=3) a[a.length-1]=line
});
console.log(a);
UPDATE
Boundary conditions: if last words/word not match any rules then just add them as last array element (but two long words cannot be newer in one string)
SUMMARY AND INTERESTING CONCLUSIONS
We get 8 nice answer for this question, in some of them there was discussion about self-describing (or self-explainable) code. The self-describing code is when the person which not read the question is able to easy say what exactly code do after first look. Sadly any of answers presents such code - so this question is example which shows that self-describing is probably a myth
(Updated to incorporate suggestion from user633183.)
I found this an interesting problem. I wanted to write a more generic version immediately, and I settled on one that accepted a list of rules, each of which described the number of words that it would gather and a test for each of those words. So with
lt6
being essentially(str) => str.length < 6
, the first rule (A) would look like this:This, it turns out, is quite similar to the solution from CertainPerformance; that answer uses strings to represent two different behaviors; this one uses actual functions. But they are quite similar. The implementation, though is fairly different.
This uses a recursive function that bottoms out when the remaining list of words is empty and otherwise searches for the first rule that matches (with, again like CertainPerformance, a default rule that simply takes the next word) and selects the corresponding number of words, recurring on the remaining words.
For simplicity, the recursive function accepts an array of words and returns an array of arrays of words. A wrapper function handles converting these to and from strings.
The only other function of substance in here is the helper function
allMatch
. It is essentially([f1, f2, ... fn], [x1, x2, ..., xn, ...]) => f1(x1) && f2(x2) && ... && fn(xn)
.Of course the currying means that
splitByRules (myRules)
returns a function you can store and run against different strings.The order of the rules might be important. If two rules could overlap, you need to put the preferred match ahead of the the other.
This added generality may or may not be of interest to you, but I think this technique has a significant advantage: it's much easier to modify if the rules ever change. Say you now also want to include four words, if they all are fewer than five characters long. Then we would just write
const lt5 = shorterThan(5)
and include the ruleat the beginning of the list.
To me that's a big win.
I also found this problem very interesting. This is a long-format answer which shows the process of how I arrived at the final program. There are several code blocks labeled
sketch
along the way. I hope for this approach to be helpful to beginners in functional style.Using the data.maybe module, I started out with -
Then I started writing some of the rules ...
Way too messy and repetitive, I thought. As the author of these functions, it's my job to make them work for me! So I restarted this time designing the rules to do the hard work -
These rules are much simpler. Next, I wanted to clean up
wordsToLines
a bit -In our initial sketch, the rules constructed a
{line, next}
object, but a higher-orderrule
means we can hide even more complexity away. And theoneOf
helper makes it easy to move our rules inline -Finally, we can write our main function,
formatSentence
-The wires are mostly untangled now. We just have to supply the remaining dependencies -
And some functional primitives -
Let's run the program -
View the complete program on repl.it and run it to see the results -