Discussion, Questions, and Resources for Part 3 of the April 2018 Cohort (Intermediate Algorithm Scripting and JavaScript Algorithms and Data Structures Projects)

TOPIC INFORMATION

See the full schedule for the April 2018 JavaScript Algorithms and Data Structures cohort.

This topic is for those of us who are participating in the April 2018 JavaScript Algorithms and Data Structures cohort. Even though this topic is mainly for those participating in the cohort, it’s totally ok for others to participate as desired. All are welcome! :national_park:

This topic covers the following sections of the fCC curriculum:

DEADLINE: 7/31/2018 11:59PM PST

The pace for this part of the cohort means that one will need to complete about one challenge per day, with maybe two days per project. There is a lot of work here, so block out some time each day to work on this and get involved in the forum to ask questions as needed, especially if you find yourself falling behind.

COHORT PROCESS

Anyone who’s participating in the cohort will want to bookmark and watch this topic.

watching

Cohort members will ask questions and share links and information with one another below. Please add to and edit this post (it’s a wiki post) as needed to share resources and other helpful information with other cohort members.

Participating in the cohort means one commits to do the following:

  • Work on and finish sections and projects by the deadline.
  • Give others feedback and reviews on their code and projects.
  • Help by answering questions as needed.
  • Share interesting information about section material and projects with the cohort.

RESOURCES

2 Likes

MEMBERS

@camper, @angelinalblyth, @yoizfefisch, @NariRoh, @gilvarry, @emilio12345, @wmooney1984, @verde79, @Asjas, @kevin0110w, @bushbass, @ecmonzon, @reginabfly, @Gigahood, @zaynaib

MEMBERS CONTINUED …

@suzannekinyenje, @nickoless, @lbeth, @ddcas, @RobGoelz, @KatFrog, @coderati, @RReddVar, @vagrahb, @wade1928, @webdevdjm, @Poojabedur, @uzorjchibuzor, @RHoneyman87, @Techne3

MEMBERS CONTINUED …

@smsguy927, @Foo_Dog, @nvrqt03, @Saddam, @clickingmouse, @brityank, @OllL, @wesam-mustafa, @jgsimmerman, @davemowery, @vbartolomei1, @catherinewoodward, @Johnrose, @mohamedkhaledyousef, @skny3d

MEMBERS CONTINUED …

@alhazen1, @deedee, @m4sterbunny, @Emgo-Dev, @JayDevelopment, @Balancedsan

Hello!

For those who get to the Telephone Validation JavaScript project, Regex can be very useful!
I had to double-back and review the Regular Expressions section, but it definitely was helpful in solving the challenge with just three lines of code.

How’s everyone doing so far with the Intermediate Scripting and Projects?

1 Like

@RHoneyman87 Thanks for the tip! I just finished Steamroller. I’m finding the intermediate algorithms to be challenging and fun. I don’t like the math-focused challenges as much though. :sunny:

Quick poll in case people are finding the challenges to be difficult or feeling rushed. I think we’ll retain and learn more if we give ourselves more time when we need it.

  • Keep Current Deadline of June 30, 2018
  • Change Deadline to July 31, 2018

0 voters

Steamroller was interesting. I spent quite a bit of time trying to figure out how to get through multiple nested arrays in JS. Google was surprisingly not as helpful as I would’ve hoped. Settled on a Recursive solution though mine was not quite right at first. I had to spend some additional time googling to finally get that one down.

Which is also my suggestion for others. Don’t be ashamed to use google and stack overflow to help you understand/solve a problem. At least in my viewpoint, in the programming world, this is not cheating and you should not feel bad for doing it, unless you copy/paste someone else’s code and don’t take the time to understand why it works. Of course, FCC even encourages this with Read-Search-Ask.

1 Like

I voted to extend - selfish reason, I moved into a new house. The last couple of weeks have been painting, packing, and a little unpacking. I haven’t started this module yet due to that :frowning: .I will be starting today or tomorrow. I do have a couple solutions already, but I’ll probably need the time.

1 Like

I want to join!..

I just finished up. If anyone has any questions along the way, please ask them!

For now, for those who attempt the cash register project, my hint is to remember the dangers of floating point math. This tripped me up several times because in floating point, 1.01 + 2.05 does not equal 3.06, it equals 3.05999999996. :frowning:

1 Like

I’m going to extend the deadline to July 31. I think this will give us more time to learn about the different ways to solve the algorithms and complete the projects. Thanks for participating in the poll!

2 Likes

Here are my first five solutions for the Intermediate Algorithm Scripting section and some commentary about my solutions and solutions I found in the fCC Guide:

Sum All Numbers in a Range

My first solution was the following, using recursion:

function sumAll(arr) {
  // Create new array to use in function. Sort array from highest to lowest.
  let newArr = arr.sort((a, b) => b - a);

  // If the difference between both numbers is zero, then return the first number in the array.
  if (newArr[0] - newArr[1] === 0) return newArr[0];

  // return the first number in the array plus the function again, but this time with the first number minus one.
  return newArr[0] + sumAll([newArr[0] - 1, newArr[1]]);
}

The fCC Guide entry for the Sum All Numbers in a Range challenge shows a solution that uses a for-loop, a solution using something called the Arithetic Progression summing formula, and the following solution that uses the spread operator (...) along with a for-loop, Math.min(), and Math.max():

function sumAll(arr) {
    var sum = 0;
    for (var i = Math.min(...arr); i <= Math.max(...arr); i++){
        sum += i;
    }
  return sum;
}

I like this final solution because it seems self-contained and not too complicated. The main part of this challenge is using the first and last numbers, and this solution does that in an elegant way. I think my solution over-complicates this challenge.

Diff Two Arrays

Here’s my solution, using filter(), arrow functions, the spread operator, and indexOf():

function diffArray(arr1, arr2) {
  let newArr = [];
  
  newArr.push(...arr2.filter(item => arr1.indexOf(item) === -1));
  newArr.push(...arr1.filter(item => arr2.indexOf(item) === -1));
  
  return newArr;
}

The fCC Guide entry for Diff Two Arrays has some interesting solutions that use includes(). The solution I like best first concatenates both arrays and then filters the concatenated array to only include items that are not in either array:

function diffArray(arr1, arr2) {
      return arr1.concat(arr2).filter(item => !arr1.includes(item) || !arr2.includes(item));
}

I moved everything into one line here, but if you want to see it broken out into multiple lines, click on the guide link above.

Seek and Destroy

My first solution used filter(), slice, and indexOf():

function destroyer(arr) {  
  return arr.filter(item => [...arguments].slice(1).indexOf(item) === -1);
}

However, after reviewing the above solutions in the guide, I tried includes() instead of indexOf() and I think it’s a little cleaner:

function destroyer(arr) {  
  return arr.filter(item => ![...arguments].slice(1).includes(item));
}

The fCC Guide entry for Seek and Destroy shows a solution using nested for-loops and one that’s close to my solution that uses includes(), but uses Array.from(arguments) which does the same thing as [...arguments].

Wherefore Art Thou

I think I cheated a little on this one. My solution doesn’t work for all possible test cases, but it passes all of the tests given for the challenge. I used Object.keys and filter():

function whatIsInAName(collection, source) {

  var arr = [];
  const keys = Object.keys(source);

  arr.push(...collection.filter(item => item[keys[0]] === source[keys[0]] && item[keys[1]] === source[keys[1]]));

  return arr;
}

If, for example, the source for the second to last test case is { "apple": 1, "bat": 2, "cookie": 3 } instead of { "apple": 1, "bat": 2 }, my solution will return [{ "apple": 1, "bat": 2 }, { "apple": 1, "bat": 2, "cookie":2 }] even though it should return an empty array since none of the objects match the source. This is because I’m only checking the first two keys and ignoring any more after that. Since it passed the tests and I was in a hurry (thinking the deadline for this section was June 30, 2018), I moved on.

The solutions in the fCC Guide for Wherefore Art Thou work correctly and as expected for all possible test cases using hasOwnProperty(). This solution in the guide that spoke to me the most:

function whatIsInAName(collection, source) {
 
  var srcKeys = Object.keys(source);

  return collection.filter(function (obj) {
    return srcKeys.every(function (key) {
      return obj.hasOwnProperty(key) && obj[key] === source[key];
    });
  });
}

With that, I was able to come up with the following similar solution using more ES6 syntax:

function whatIsInAName(collection, source) {

  const keys = Object.keys(source);

  return collection.filter(collectionItem => keys.every(key => collectionItem.hasOwnProperty(key) && collectionItem[key] === source[key]));

}

I hope I remember to use this filter() - every() combination in the future!

Spinal Tap Case

For this one, I had two solutions which basically combined split() and join() in various ways with regular expressions. Here’s one of them:

function spinalCase(str) {
  str.split(/[ _]/g).map(item => item.split(/(?=[A-Z])/g)).join('-').toLowerCase().replace(/[,]/g, '-');
}

Interestingly, the solutions in the fCC Guide for Spinal Tap Case don’t seem to pass all of the tests anymore.

@Velenir and the fCC Forum to the rescue. :smile: @Velenir’s solution passes all of the tests and uses a nice regular expression to do it in one line:

function spinalCase(str) {
  return str.split(/[-\s_]|\B(?=[A-Z])/).join('-').toLowerCase();
}

I’ll have to come back to this one when I have some time to truly understand how that regular expression is working. If anyone understands, please explain! Thanks.

Anyone come up with any other interesting ways to solve these challenges? :sunny:

1 Like

You could simply your Seek and Destroy a little more and avoid using slice with:

const destroyer = (arr, ...args) => arr.filter(item => !args.includes(item));

Also, for the Sum All Numbers in a Range challenge, your sumAll function, since let newArr = arr.sort((a, b) => b - a); does not actually create a new array and actually modifies the array passed into the function, you can simply just sort arr and then use some destructuring to make your solution more readable. Finally, you can use the ternary operator to combine the to two return statements into one.

const sumAll = arr => {
  const [max, min] = arr.sort((a, b) => b - a);
  return max === min ? max : (max + sumAll([max - 1, min]));
};
1 Like

Nothing that different here - mostly just slight variations of the same algorithms. I am trying to stretch my ES6 wings a little

Sum All Numbers in a Range

Here I did cache values of larger, smaller to limit calls to Math.max and Math.min

function mySumAll(arr){
  const [smaller, larger] = [Math.min(...arr), Math.max(...arr)];
  let sum = 0;
  
  for(let i = smaller; i <= larger; i++){
    sum += i;
  }
  return sum;
}
Diff Two Arrays
function diffArray(arr1, arr2) {
  const halfDiff = (a1, a2) => a1.filter(el => !a2.includes(el));
  return [].concat(halfDiff(arr1, arr2)).concat(halfDiff(arr2, arr1));
}
Seek and Destroy

Got to try out rest parameters

function destroyer(arr, ...args) {
  return arr.filter(element => args.indexOf(element) === -1);
}
Wherefore art thou

I’ll have to read the guide. I did not see that we needed .hasOwnProperty - I was thinking it either passed or it failed the test for filter.

function whatIsInAName(collection, source) {
  var arr = [];
  // Only change code below this line
  const keys = Object.keys(source);
  arr = collection.filter( el => keys.every(key => el[key] === source[key]) );
  // Only change code above this line
  return arr;
}
Spinal Tap Case

My regex solution was not as sweet as Velenir’s solution. (I actually have to look at my notes to remember exactly what it does anyways). It did not occur to me to use .split(). I had forgotten that split takes a regex.

function spinalCase(str) {
  return str
  .replace(/([a-z])([A-Z])/g,'$1 $2')
  .replace(/[\s_]/g,'-')
  .toLowerCase();
}
2 Likes

@RandellDawson Thank you for your input here! I especially like your ES6 syntax for functions (const functionName = argument => {}) as that’s an excellent reminder of a different (better?) way to write them. :exclamation:

@alhazen1 I think you’re right! I remember thinking something similar but forgot about it. It seems to work without first checking to see if the property exists in all of my tests. Also, I like your Spinal Tap Case solution. Thanks for sharing! :sunny:

Here are my solutions for the next three, with some commentary and questions:

Pig Latin

My solution uses search() and slice(). Using ternary operators, it’s mostly a one-line solution. I don’t like the solutions in the guide because they seem more complicated than is needed.

function translatePigLatin(str) {
  // Find the index of the first vowel
  let vowelLocation = str.search(/[aeiou]/);

  /* If the index of the first vowel is at the beggining of
     the word `(vowelLocation === 0)`, return the string with 'way'
     added to the end.

     If the string has no vowels `vowelLocation === -1`, return
     the string with 'ay' added to the end.

     If the first vowel is somewhere else, slice the string
     at the index of the first vowel leaving all of the
     letters of the string following and including the first vowel
     `str.slice(vowelLocation)`. Then, add the letters before the
     first vowel to the end of the sliced string 
     `str.slice(0, vowelLocation)` along with 'ay' at the very end.
  */
  return vowelLocation === 0 ? str + 'way' : 
         vowelLocation === -1 ? str + 'ay' : 
          str.slice(vowelLocation) + str.slice(0, vowelLocation) + 'ay';
}
Search and Replace

For this one, I was able to solve it using slice() and replace(). The solutions in the guide were either overly complicated or similar to what I came up with but not in one line.

function myReplace(str, before, after) {
  return before[0].toUpperCase() === before[0] ? str.replace(before, after[0].toUpperCase() + after.slice(1)) : str.replace(before, after);
}
DNA Pairing

I did this one the easy way, by checking for every possible situation and returning the appropriate result. I used split() and map().

function pairElement(str) {
  return str.split('').map(element => element === 'A' ? ["A", "T"] : element === 'C' ? ["C", "G"] : element === 'G' ? ["G", "C"] : ["T", "A"]);
}

After looking at the fCC Guide for DNA Pairing, I was able to create the following which levarages an object and is much cleaner, in my opinion. It’s based on the Intermediate Solution in the guide:

const pairElement = str => {
      const dnaMap = {T:'A', A:'T', G:'C', C:'G'};
      return str.split('').map(element => [element, dnaMap[element]]);
}

I think this can be even more simplified by removing the dnaMap variable:

const pairElement = str => str.split('').map(element => [element, {T:'A', A:'T', G:'C', C:'G'}[element]]);

Does anyone know if creating a temporary object like this is significantly different from storing it in a variable?

How did others solve these? :sunny:

1 Like

Here are the next three. :sunny:

Missing Letters

I matched the string to a string containing every letter of the alphabet, then looped through each letter and placed the missing letter in a result variable.

function fearNotLetter(str) {
  const letters = "abcdefghijklmnopqrstuvwxyz";
  let letterArr = letters.split('').splice(letters.indexOf(str[0]), str.length + 1);
  let counter = 0;
  let result = [];

  letterArr.map(letter => {
    if (str[counter] !== letter) result.push(letter);
    counter++;
  });

  return result[0];
}

The fCC Guide for Missing Letters compares letters using character codes with charCodeAt():

function fearNotLetter(str) {
  for(var i = 0; i < str.length; i++) {
    var code = str.charCodeAt(i);

    /*
       If the current character code does not equal the current letter's
       character code, then return the previous character code.
     */
    if (code !== str.charCodeAt(0) + i) {
      return String.fromCharCode(code - 1);
    }  
  }
  return undefined;
}

It’s less code and doesn’t need all of the variables that my solution needs. From that, I was able to come up with this using filter():

function fearNotLetter(str) {
  let firstCharacter = str.charCodeAt();
  
  let notEqual = str.split('').filter((letter, letterIndex) => letter !== String.fromCharCode(firstCharacter + letterIndex))[0];

  return notEqual === undefined ? undefined : String.fromCharCode(notEqual.charCodeAt() - 1);
}

I’m not sure which way is best. They all work.

Sorted Union

I solved this one using map(), Set(), and the spread operator:

function uniteUnique(arr) {
  let allArrays = [];
  [...arguments].map(insideArr => allArrays.push(...insideArr));

  return [...new Set(allArrays)];
}

Apparently, this can be done with even less code, according to the fCC Guide on Sorted Union:

function uniteUnique(arr) {

  //make an array out of arguments and flatten it (using the spread operator)
  const args = [].concat(...arguments);

  // create a Set
  return [...new Set(args)];
}
Convert HTML Entities

I used regular expressions and replace(), which almost matches the intermediate code solution in the fCC Guide for Convert HTML Entities:

function convertHTML(str) {
  return str.replace(/&/g, '&amp;')
            .replace(/>/g, '&gt;')
            .replace(/</g, '&lt;')
            .replace(/'/g, '&apos;')
            .replace(/"/g, '&quot;');
}

The guide also has a solution that uses an object. I sensed this was possible and attmepted it, but didn’t solve it that way. I did modify the guide’s solution to use an arrow function:

function convertHTML(str) {
  const htmlEntities={
    '&':'&amp;',
    '<':'&lt;',
    '>':'&gt;',
    '\"':'&quot;',
    '\'':"&apos;"
  };

  return str.split('').map(entity => htmlEntities[entity] || entity).join('');
}

Either way seems to work. It’s trivial to add more properties/conditions for both solutions, however, the object-oriented solution seems to be more flexible.

not much different on the first 3 challenges, but I’m having a tough time with the 4th - wherefore art though… :sob:

@nvrqt03 It took me a while too. You’ll get it. :+1: It might help to explain how you think you can solve it here and we can help fill in gaps.

1 Like