Mutations challenge in JavaScript

I am trying solving a challenge called Mutations from Basic Algorithm Scripting section on freeCodeCamp.com Curriculum.
link to the challenge:
https://www.freecodecamp.org/learn/javascript-algorithms-and-data-structures/basic-algorithm-scripting/mutations
I checked my code many times but I do not know what are the mistakes that in my code.

My code so far:

function mutation(arr) {
  let arr0 = arr[0].toLowerCase();
  let arr1 = arr[1].toLowerCase();
  let target0 = arr0.split("");
  let target1 = arr1.split("");
  let array = [];
  array.push(target0);
  array.push(target1);
  for (let i = 0; i < target1.length; i++) {
    if (target0.indexOf(target1[i] >= 0)) {
      return true;
    }
    else {
      return false;
    }
  }
  return array;
}

Return true if the string in the first element of the array contains all of the letters of the string in the second element of the array.

Lastly, ["Alien", "line"] , should return true because all of the letters in “line” are present in “Alien”.

A - l - i - e - n

l - i - n - e

Something seems wrong here…

As far as I can see there is no “a” in line.

What wrong there? From the word “line”, l is in Alien, i is in Alien, n is in Alien, and e is in Alien.

Are you possibly interpreting things the other way?

First element of the array matches all letters of the second.

1st = alien
2nd = line

There is no “a” in line.

Yes, it seems that you are currently observing Opposite Day.

The description is: Return true if the string in the first element of the array contains all of the letters of the string in the second element of the array.

The string “Alien” contains all of the letters of the string “line”.

Ops… yeah you are right.

1 Like

At times like this I find that it’s vital to remember that feeling just before you found out. It feels exactly the same when you are incorrect but don’t know it yet, as it does to be right, and that’s so dangerous.

I find that it’s a good reminder for me to take a break for a bit. Congrats on handling it well.

const mutation = arr => {
  const lowered = arr.map(v => v.toLowerCase());
  const item1 = lowered[0].split('');
  const item2 = lowered[1].split('');
  const diff = item2.filter(v => item1.includes(v));
  return diff.length === item2.length;
}

mutation(["hello", "hey"]);

The power of JavaScript is using those array functions to your advantage.

1 Like

This is fun to.

const mutation = arr => arr[1].toLowerCase().split('').filter(v => arr[0].toLowerCase().split('').includes(v)).length === arr[1].length;

mutation(["hello", "hey"]);

Just to join in the fun

const included = (a, [b, ...rest]) =>
  (a.indexOf(b) === -1)
    ? !(b)
    : included(a, rest)

with lower case

const included = (a, b) =>
  (function includes(a, [b, ...rest]) {
    return (a.indexOf(b) === -1)
      ? !(b)
      : includes(a, rest)
  }(a.toLowerCase(), b.toLowerCase()))

console.log(included('Rodent', 'deNt')) // true

Seems to work :slight_smile:

What is the point of writing function in JavaScript code these days? I’ve been using typescript for the last two years but I know that browsers have supported arrow functions now for several years. Many linters by default prefer const with arrow functions that than a declaration with function.

You’re a ray of sunlight.

Because it’s recursive and it was a convenient way to name a function?

Just a bit of fun, while my computer was rendering.

Have to admit my variation is not just a bit ugly, but flawed. If the second word is an empty string it passes as true.

Edit: As per Paul’s comment below
I think you could add an early return if the second word is longer than the first. An amended version

// destructure the passed array's two values
const mutation = ([wordA, wordB]) => {

  const includes = (a, [b, ...rest]) =>
    (a.indexOf(b) === -1)
      ? b === undefined // maybe clearer than !(b)
      : includes(a, rest)

  // if 'wordB' is an empty string return false
  return (!wordB)
    ? false
    : includes(wordA.toLowerCase(), wordB.toLowerCase())
}

console.log(mutation(['hello', 'hello'])) // true
console.log(mutation(['hello', 'hey'])) // false
console.log(mutation(['alien', 'line'])) // true

Just to do a breakdown of [b, ...rest] in the includes function.

const includes = (a, [b, ...rest]) => {
    console.log(b, rest)
    return (a.indexOf(b) === -1)
      ? b === undefined
      : includes(a, rest)
  }

log output for each call to includes with [‘hello’, ‘hello’]

b ...rest
h ['e','l','l','o']
e ['l','l','o']
l ['l','o']
l ['o']
o []
undefined []

b is undefined so all the letters have been used and matched → true

log output for each call to includes with [‘hello’, ‘hey’]

b ...rest
h ['e', 'y']
e ['y']
y []

b is ‘y’ so not undefined → false

Does that cause another problem if all of the letters in the second longer word are in the first word?

For example, [“Alien”, “lineal”]
That should still pass.

However, if the second word has more unique letters than the first, that is a good test for an early exit.

You are absolutely right. I had to go back and re-read the question

I think an edit is coming up. oops

1 Like

And I just realized, checking only those unique letter against each other results in a fully valid solution to the problem too.

1 Like

My brains are a bit foggy this morning, but yes absolutely.

I’m thinking with Set possibly

const strg = [...'missed that!']
const unique = new Set(strg)
console.log([...unique].join('')) // mised tha!
1 Like

And if you merge two sets together, you can check if it’s larger than it began, allowing you to gain successful results without needing to check each one individually.

const secondSet = new Set(second);
const merged = new Set([...unique, ...secondSet]);
return merged.size === unique.size;
1 Like

I’m trying to keep up with you here Paul. LOL

const unique = arr => [...(new Set([...arr]))]

const mutation = ([wordA, wordB]) => {

  const includes = (a, [b, ...rest]) =>
    (a.indexOf(b) === -1)
      ? b === undefined
      : includes(a, rest)

  return (!wordB || wordB.length > wordA.length)
    ? false
    : includes(unique(wordA.toLowerCase()), wordB.toLowerCase())
}

Thanks @rpg_digital for the Set idea.

Here’s my working code:

function mutation(arr) {
  const firstSet = new Set(arr[0].toLowerCase());
  const secondSet = new Set(arr[1].toLowerCase());
  const merged = new Set([...firstSet, ...secondSet]);
  return firstSet.size === merged.size;
}
1 Like