0

I have a bad word that contains illegal letters. I have a list of legal letters (which can be more than one character long). The following nested loop iterates over both the characters in the word and the legal letters, replacing any illegal letter with null.

In order to both retain the legal letters and substitute illegal ones, it's important that all legal letters are looped through. The illegal substitution would happen once that loop finishes.

// acceptable letters:
const legalLetters = ["ND", "CH", "S"]
// bad, evil word containing unacceptable letters:
let word = "SANDWICH"

const filteredLetters = []

while (word.length > 0) {
  for (const letter of legalLetters) {
    if (word.startsWith(letter)) {
      // remove that many letters from the start of the word
      word = word.slice(letter.length)
      filteredLetters.push(letter)
      // break back to the while loop to re-scan the truncated word
      break
    }
  } else {
    // this is the part I'm having trouble with
    // if the word does not start with an acceptable letter, remove that letter
    word = word.slice(1)
    filteredLetters.push(null)
  }
  // some filteredLetter was added and the length of the word has been reduced
  // repeat until the word is all gone
}
console.log(filteredLetters) // should be ["S", null, "ND", null, null, "CH"]

In the above example I've used Python's for ... else construct, which executes the code in the else block only if there was no break in the for block. Such a syntax does not exist in Javascript (and therefore the snippet above is nonsense).

How would I go about creating this 'default behaviour' in Javascript?

Lodash and co. answers are okay for my purposes, and this may well be an XY problem, so I welcome any restructuring advice.


Related question: For else loop in Javascript?

The answers to this question recommend either setting a boolean flag or breaking to a label. I'd prefer to avoid these approaches, if possible - the flag approach feels messy, creating an unnecessary variable, and the label approach just doesn't feel quite right.

snazzybouche
  • 2,241
  • 3
  • 21
  • 51
  • You can set a boolean, or use a loop counter whose value can be tested after the loop (if it's less than the max value then the loop was quit early). – jarmod Jul 08 '20 at 17:23
  • You need a boolean flag or a label to emulate that functionality in JavaScript. – Unmitigated Jul 08 '20 at 17:23
  • Does this answer your question? [For else loop in Javascript?](https://stackoverflow.com/questions/21525282/for-else-loop-in-javascript) – jarmod Jul 08 '20 at 17:23
  • @jarmod That question is already linked in my question as an example of an approach I'd prefer to avoid – snazzybouche Jul 08 '20 at 17:24
  • Assuming that your question is the generic "how can I implement for/else in JS", then I think all the options available to you are documented here and in the other answer. Let's try this: why are you unhappy with the available options? What criteria do you have for a 'better' solution (that doesn't exist afaik)? If your question is "how can I write this specific piece of code better?", then that's a different question. – jarmod Jul 08 '20 at 17:48
  • Unless a loop is somehow important to the solution, it seems like a basic regular expression could simplify this into a few lines. `let word = "SANDWHICH"; let cleaned = word.replace(/[^(ND|CH|S)]+/gu,''); console.log(cleaned); ` If you do want to go this route, it will need a few tweaks to be 100% accurate but it is a simpler solution. – imvain2 Jul 08 '20 at 18:05
  • @jarmod I suppose it's subjective - they just feel messy to me, little bits dangling everywhere. Perhaps it's my experience with Python - I'm really looking for everything to be nicely integrated with itself. Maybe it's unattainable! – snazzybouche Jul 08 '20 at 18:29
  • @imvain2 The correct and intended output of the code is `["S", null, "ND", null, null, "CH"]` - regex is great for string substitution, and would be the easiest approach if I were concatenating the result back to a string, but this goes a little beyond that. – snazzybouche Jul 08 '20 at 18:31

1 Answers1

1

I hope this can help you.

// acceptable letters:
const legalLetters = ["ND", "CH", "S"]
// bad, evil word containing unacceptable letters:
let word = "SANDWICH"

const filteredLetters = []

while (word.length > 0) {
  let pushedItem = null;
  for (const letter of legalLetters) {
    if (word.startsWith(letter)) {
      pushedItem = letter;
      break
    }
  } 
  word = word.slice(pushedItem ? pushedItem.length : 1)
  filteredLetters.push(pushedItem)
}
console.log(filteredLetters) 
Ehsan Nazeri
  • 771
  • 1
  • 6
  • 10
  • 1
    The `pushedItem` approach is not a bad idea, thank you for the tip. I wonder why `letter === word.substr(0, letter.length)` is needed, though? It seems to be the same as `word.startsWith(letter)` – snazzybouche Jul 08 '20 at 18:33
  • you are right. I used diffrent algorithm at first then I changed it. i will fix it – Ehsan Nazeri Jul 08 '20 at 18:36