0

Is there a way to match multiple words from a paragraph while omitting commas, full stops, spaces and case sensitivity? Better yet, return the answer with a list of ascending number in front.

I received this forwarded message during Christmas and thought I'd have some fun using code to solve it.

const paragraph = "I once made a remark about the hidden books of the Bible. A certain luke, kept people looking so hard for facts, and for others, it was a revelation. Some were in a jam, especially since the names of the books were not capitalized. But the truth finally struck home to numbers of our readers. To others it was a job. We want it to be a most fascinating little moment for you. Yes, there will be some really easy ones to spot. Others may require judges to help find them. I will be quickly admit it usually takes a minister to find one of them, and there will be loud lamentations when it is found. A little lady says she brews a cup of tea so she can concentrate better. See how well compete. Relax now, for there really are sixteen books of the Bible in this paragraph."

function findBooks() {
  let newParagraph = paragraph.toLowerCase();
  let regEx = /[.,\s]/g;
  let workingPara = newParagraph.replace(regEx, '');
  let matches = workingPara.match(/(genesis|exodus|leviticus|numbers|deuteronomy|joshua|judges|ruth|samuel|kings|chronicles|ezra|nehemiah|esther|job|psalms|proverbs|ecclesiastes|songofsolomon|isaiah|jeremiah|lamentations|ezekiel|daniel|hosea|joel|amos|obadiah|jonah|micah|nahum|habakkuk|zephaniah|haggai|zechariah|malachi|matthew|mark|luke|john|acts|romans|corinthians|galatians|ephesians|philippians|colossians|thessalonians|timothy|titus|philemon|hebrews|james|peter|john|jude|revelation)/g).join(', ');

  return matches;

  }

  findBooks();

I was thinking of listing the Bible books as an array and match them to the message but found I could only do one book at a time, and i didn't want to manually check it one by one. I thought of looping through the array but it didn't work. I know the final answer to be 16 and wanted to added a number list infront of the answer but again, it didn't work. Also wanted to capitalize the first letter of the book but realise that the whole answer is an entire string, so couldn't use the charAt[0].toUpperCase() method.

Happy enough with the answer generated from my code but an enhancement would be better. (eg 1 Mark, 2 John, etc)

Paddlepop
  • 25
  • 5
  • Why don't you put your text in a external textile and make your program read it. It is very ugly and inconvenient to put the text in the source code. – roschach Dec 30 '18 at 21:51
  • Is it because it is very long? I thought it's supposed to go into the source code because it is part of the code. Self learning javascript from FreeCodeCamp here. Thanks for the suggestion, I'll take note of it next time I have a long paragraph. – Paddlepop Dec 31 '18 at 07:21
  • Well if it is so in the tutorial then go along with it...I prefer to put in external files because especially when a source code is compiled I can change the external file without recompiling. Also if I open the code in the terminal is more compact...In JavaScript maybe you don't have to worry about it so don't worry – roschach Dec 31 '18 at 09:47

3 Answers3

0

You could make use of regular expressions' word boundaries (\b) instead. Your code becomes:

return text.match(new RegExp('\\b(?:genesis|exodus|...|revelation)\\b', 'gi'));

The g flag retrieves all matches across the string, and the i flag makes it case-insensitive.

I'd also suggest storing the list of books in an array, and returning an array, which is more flexible. This also lets you use the index for the position each word appeared at.

const paragraph = `I once made a remark about the hidden books of the Bible. A certain luke, kept people looking so hard for facts, and for others, it was a revelation. Some were in a jam, especially since the names of the books were not capitalized. But the truth finally struck home to numbers of our readers. To others it was a job. We want it to be a most fascinating little moment for you. Yes, there will be some really easy ones to spot. Others may require judges to help find them. I will be quickly admit it usually takes a minister to find one of them, and there will be loud lamentations when it is found. A little lady says she brews a cup of tea so she can concentrate better. See how well compete. Relax now, for there really are sixteen books of the Bible in this paragraph.`;

function findBooks(text) {
  const books = ['genesis', 'exodus', 'leviticus', 'numbers', 'deuteronomy', 'joshua', 'judges', 'ruth', 'samuel', 'kings', 'chronicles', 'ezra', 'nehemiah', 'esther', 'job', 'psalms', 'proverbs', 'ecclesiastes', 'songofsolomon', 'isaiah', 'jeremiah', 'lamentations', 'ezekiel', 'daniel', 'hosea', 'joel', 'amos', 'obadiah', 'jonah', 'micah', 'nahum', 'habakkuk', 'zephaniah', 'haggai', 'zechariah', 'malachi', 'matthew', 'mark', 'luke', 'john', 'acts', 'romans', 'corinthians', 'galatians', 'ephesians', 'philippians', 'colossians', 'thessalonians', 'timothy', 'titus', 'philemon', 'hebrews', 'james', 'peter', 'john', 'jude', 'revelation'];
  return text.match(new RegExp('\\b(?:' + books.join('|') + ')\\b', 'gi'));
}

const foundBooks = findBooks(paragraph);
console.log(foundBooks);

const foundBooksPositions = foundBooks
  .map((book, position) => `${position + 1}: ${book}`)
  .join('\n');
console.log(foundBooksPositions);
Jeto
  • 14,596
  • 2
  • 32
  • 46
  • The OP said their code was working, they just wanted numbers. Your code is a good alternative solution to the problem though. – Jack Bashford Dec 30 '18 at 18:05
  • @JackBashford The title of the question may imply that she's looking for any kind of improvement (though one could argue SO might not be the most fitting place for this kind of ask). – Jeto Dec 30 '18 at 18:08
  • Thank you for your help. I'm not familiar with the map method but I'll study to see how your code works. – Paddlepop Dec 31 '18 at 07:39
0

If all you want is an ascending series of numbers, one before each book name (in the array) you could just loop over the array and add an incremental counter to the front like so:

var matches = findBooks();
matches.forEach((book, index, arr) => {
    arr[index] = (index + 1) + " " + book;
});
Jack Bashford
  • 43,180
  • 11
  • 50
  • 79
0
const paragraph = "I once made a remark about the hidden books of the Bible. A certain luke, kept people looking so hard for facts, and for others, it was a revelation. Some were in a jam, especially since the names of the books were not capitalized. But the truth finally struck home to numbers of our readers. To others it was a job. We want it to be a most fascinating little moment for you. Yes, there will be some really easy ones to spot. Others may require judges to help find them. I will be quickly admit it usually takes a minister to find one of them, and there will be loud lamentations when it is found. A little lady says she brews a cup of tea so she can concentrate better. See how well compete. Relax now, for there really are sixteen books of the Bible in this paragraph.";

function findBooks() {
    // regexp can be told to match case-insensitively by using the i flag
    const workingPara = paragraph.replace(/[., ]/ig, '');
    const matches = workingPara.match(/(genesis|exodus|leviticus|numbers|deuteronomy|joshua|judges|ruth|samuel|kings|chronicles|ezra|nehemiah|esther|job|psalms|proverbs|ecclesiastes|songofsolomon|isaiah|jeremiah|lamentations|ezekiel|daniel|hosea|joel|amos|obadiah|jonah|micah|nahum|habakkuk|zephaniah|haggai|zechariah|malachi|matthew|mark|luke|john|acts|romans|corinthians|galatians|ephesians|philippians|colossians|thessalonians|timothy|titus|philemon|hebrews|james|peter|john|jude|revelation)/ig);

    return matches.reduce((acc, book, idx) => {
        const numbering = idx + 1;

        // perform the toLowerCase method here instead of lowerCasing the whole paragraph
        // because the length of the paragraph may be very long, performing the method
        // here saves some computation time, although negligible in most cases
        const lowerCasedBook = book.toLowerCase();
        const capitalizedBook = lowerCasedBook[0].toUpperCase() + lowerCasedBook.slice(1);

        // yields books with numberings, e.g. 1 Mark
        if (idx !== matches.length - 1) {
            return acc += `${numbering} ${capitalizedBook}, `;
        } else {
            return acc += `${numbering} ${capitalizedBook}`;
        }
    }, '');
}

console.log(findBooks());
Ray Chan
  • 1,050
  • 9
  • 18
  • Thanks for the suggestion, your code returns exactly what I have in mind. I can't say I fully understand your code (not sure what is acc and idx) but I'll study and make sense of it. – Paddlepop Dec 31 '18 at 07:24
  • I used the array's reduce method, its callback function will get 4 arguments, the first one is the accumulator(acc), second one is the value. Essentially, the reduce function loops through the array and in most cases, try to "reduce" to a single value. You can read more about this method in [link to Array.prototype.reduce()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce) – Ray Chan Dec 31 '18 at 07:56