CoDEVIANT #4 (3/24/19)

Adrian Rosales
11 min readMar 24, 2019

Ok…back at this shit again. Of all the habits I have, this seems to at least be healthier than most.

Me, this past weekend.

Today I’m starting off with a math based one. It’s not something I chose on purpose…I just hit random when I go to CodeWars. Typically, I’d run away from math based ones…but I’m going to try thugging it out. Plus at least this one doesn’t seem to deal with any concepts I’ve never heard of before, so that’s a plus.

Buying a Car

Instructions:

A man has a rather old car being worth $2000. He saw a secondhand car being worth $8000. He wants to keep his old car until he can buy the secondhand one.

He thinks he can save $1000 each month but the prices of his old car and of the new one decrease of 1.5 percent per month. Furthermore this percent of loss increases by 0.5 percent at the end of every two months. Our man finds it difficult to make all these calculations.

Can you help him?

How many months will it take him to save up enough money to buy the car he wants, and how much money will he have left over?

Parameters and return of function:

parameter (positive int, guaranteed) startPriceOld (Old car price)parameter (positive int, guaranteed) startPriceNew (New car price)parameter (positive int, guaranteed) savingperMonthparameter (positive float or int, guaranteed) percentLossByMonthnbMonths(2000, 8000, 1000, 1.5) should return [6, 766] or (6, 766)where 6 is the number of months at the end of which he can buy the new car and 766 is the nearest integer to 766.158 (rounding 766.158 gives 766).

Note:

Selling, buying and saving are normally done at end of month. Calculations are processed at the end of each considered month but if, by chance from the start, the value of the old car is bigger than the value of the new one or equal there is no saving to be made, no need to wait so he can at the beginning of the month buy the new car:

nbMonths(12000, 8000, 1000, 1.5) should return [0, 4000]nbMonths(8000, 8000, 1000, 1.5) should return [0, 0]

We don’t take care of a deposit of savings in a bank:-)

How Adrian will attempt/solve this problem:

Real talk…I don’t always solve it first and then give a presentation. I almost always laboriously detail my thought process as I do it while jumping on and off stage for opera rehearsals. I may not, and likely won’t, be able to figure this out. But I’m going to try my level best.

What we are given to work with:

function nbMonths(startPriceOld, startPriceNew, savingperMonth, percentLossByMonth){   //your code here}

Our function is supposed to be able to pass tests like this:

Test.assertSimilar(nbMonths(2000, 8000, 1000, 1.5), [6, 766])Test.assertSimilar(nbMonths(12000, 8000, 1000, 1.5) ,[0, 4000])

What this means is that our guy in the problem would be able to buy the new car in 6 months with 766 dollars left over.

Or that in another reality, he could buy it immediately with 4000 dollars left over.

Adrian’s Observations:

function nbMonths(startPriceOld, startPriceNew, savingperMonth, percentLossByMonth){   console.log(startPriceOld, startPriceNew, savingperMonth,     percentLossByMonth);//So we know this cat can save a grand a month. I know who to rob now.//Let’s make a variable starting at 0 that represents this cat’s dough.var dough = 0;//We will need a counter for months…each 1 month savingsPerMonth increases by 1000 & the cost of the new car decreases by 1.5%//& each 2 months the price of the new car decreases 0.5 %let month;//We don’t give a shit about the price of the old car…that seems like a red herring to me//We know that every two months, the value of the variable startPriceNew is going to decrease by 1.5%//& each 2 months the price of the new car decreases 0.5 %//I’m going to be gutsy and directly manipulate the argument instead of making a variable with the initial//new car price to manipulate.//So while dough is less than the price of the new car, we will increase the months by 1,//when we do this, we will augment the dough by 1000 and make the new car price equal itself minus 1.5% of itself.//and whenever//months is divisible by 2 without any remainder (the even months) we will again increase the dough by 1000//& add 0.5 to the argument percentLossByMonth//Once dough is not less than the price of the new car, we’ll stop and divide the price of the new car by our dough.//we’ll round the remainder and put both the result of the division and the remainder in an array, and call it day…we hope.}
function nbMonths(startPriceOld, startPriceNew, savingperMonth, percentLossByMonth){console.log(startPriceOld, startPriceNew, savingperMonth, percentLossByMonth);//So we know this cat can save a grand a month. I know who to rob now.//Let’s make a variable starting at 0 that represents this cat’s dough.//I also forgot that we need to mention the value of the old car because he is going//to get credit for it…var dough = 0 + startPriceOld;//so really dough should include the value of startPriceOld and how that argument will change overtime too.//We will need a counter for months…each 1 month savingsPerMonth increases by 1000 & the cost of the new car decreases by 1.5%//& each 2 months the price of the new car decreases 0.5 %let month = 0;//We know that every two months, the value of the variable startPriceNew is going to decrease by 1.5%//& each 2 months the price of the new car decreases 0.5 %//I’m going to be gutsy and directly manipulate the argument instead of making a variable with the initial//new car price to manipulate.//So while dough is less than the price of the new car, we will increase the months by 1,//when we do this, we will augment the dough by 1000 and make the new car price equal itself minus 1.5% of itself.//and whenever//months is divisible by 2 without any remainder (the even months) we will again increase the dough by 1000//& add 0.5 to the argument percentLossByMonthwhile(dough < startPriceNew) {console.log(‘dough is smaller than the price of the new car’);month++;dough += 1000;//no matter what happens, our guy gets another grand each monthswitch(month%2 == 0) {case true://the stuff we do every two monthsconsole.log(‘even month’);console.log(‘month is: ‘ + month);percentLossByMonth += 0.5;startPriceNew = startPriceNew — (startPriceNew * percentLossByMonth/100);dough -= (startPriceOld * percentLossByMonth/100);startPriceOld -= (startPriceOld * percentLossByMonth/100);console.log(‘2nd months percent:’ + percentLossByMonth);break;default://the stuff we do every regular monthconsole.log(‘month is: ‘ + month);startPriceNew = startPriceNew — (startPriceNew * percentLossByMonth/100);dough -= (startPriceOld * percentLossByMonth/100);startPriceOld -= (startPriceOld * percentLossByMonth/100);console.log(‘1st months percent:’ + percentLossByMonth);break;}console.log(‘Dough on hand: ‘ + dough);console.log(‘Price of the New Car at month ‘ + month + ‘: ‘ + startPriceNew);}if(dough >= startPriceNew){console.log(‘saved up enough’);console.log(dough — startPriceNew);return [month, Math.round(dough-startPriceNew)];}//Once dough is not less than the price of the new car, we’ll stop and divide the price of the new car by our dough.//we’ll round the remainder and put both the result of the division and the remainder in an array, and call it day…we hope.}

And you know what? This almost works.

So close…yet so far…

Turns out that the writing of the test was a little misleading. We get directions that the man thinks he can save $1000 a month. However, the function we are tasked with filling out includes an argument that dictates how much the man will save per month:

savingperMonth

So if I change this line:

dough += 1000;

to

dough += savingperMonth;

then we win!

Boo-FUCKIN-Yah!

Adrian’s Solution:

function nbMonths(startPriceOld, startPriceNew, savingperMonth, percentLossByMonth){var dough = 0 + startPriceOld;let month = 0;while(dough < startPriceNew) {   month++;   dough += savingperMonth;   switch(month%2 == 0) {      case true:         percentLossByMonth += 0.5;         startPriceNew = startPriceNew — (startPriceNew *     percentLossByMonth/100);         dough -= (startPriceOld * percentLossByMonth/100);         startPriceOld -= (startPriceOld * percentLossByMonth/100);    break;    default:       startPriceNew = startPriceNew — (startPriceNew *   percentLossByMonth/100);      dough -= (startPriceOld * percentLossByMonth/100);      startPriceOld -= (startPriceOld * percentLossByMonth/100);    break;  }}   if(dough >= startPriceNew){    return [month, Math.round(dough-startPriceNew)];   }}

Let’s break it down:

  • dough represents the money the man has on hand PLUS the value of his old car.
  • month represents the amount of months that have passed since homeboy began saving up for his new whip.
  • While dough is less than the price of the new car:
  • month increases by one
  • dough increases by the value represented by the savingperMonth argument that is passed into the function.
  • We use a modulus operator to figure out if we are dealing with every second month or just another regular month, because each second month has different effects on the money flow of our prospective car buyer.
*adjusts glasses*

Modulus:

Let me explain. When you divide 7/3, you’re going to get a remainder with that. If you just wanted to get the remainder of a given set of numbers being divided, you would express it like this:

console.log( 7 % 3 ); // we get a remainder of 1…so you’ll just get 1console.log( 11 % 3); //we get a remainder of 2…you will then get 2.

In our solution, we see if we get a remainder of any kind when we divide by 2…if we do not (if it equals zero), then we know we are dealing with a second month, which impacts the man’s money situation a little bit differently.

Back to the solution:

If we are in a second month:

  • We increase the amount of percentage lost per month by 0.5
  • The new car’s price [startPriceNew] equals itself minus (itself * the percentage loss each month)
  • dough equals itself minus the (old car’s value * the percentage loss each month)
  • The old car’s value also becomes itself minus its value multiplied by the percentage loss each month.
  • If we are in a regular month:
  • The exact same stuff happens EXCEPT that the percentage lost per month value remains the same.
  • Once dough is finally more than or equal to whatever the price of the new car happens to be:
  • we return an array with the month value at the first index location, and the rounded difference of the dough subtracted from the price of the new car at the second index location.
smh

How do n̵e̵r̵d̵s̵ really good programmers do it?

function nbMonths(startPriceOld, startPriceNew, savingperMonth, percentLossByMonth) {var months = 0, moneySaved = 0;while (startPriceNew > startPriceOld + moneySaved){   moneySaved += savingperMonth;   startPriceOld -= (startPriceOld * (percentLossByMonth / 100));   startPriceNew -= (startPriceNew * (percentLossByMonth / 100));   months++;   if (months % 2 == 1) {      percentLossByMonth += .5;   }}return [months, Math.round(startPriceOld + moneySaved — startPriceNew)]; }

Hm…okay, so I see a lot of similarities (not to toot my own horn…too much 😉 )

  • Our dough variable and the others’ moneySaved variable serve the exact same purpose.
  • Great minds do think alike.
  • We use a while loop, although the direction of the compared values was reversed in mine.
  • The others’ also used similar lines to mine to change the value of dough/moneySaved, startPriceOld, & startPriceNew
  • months get augmented LATER in the process, which is why the others’ solution seeks for a remainder of 1 when using the modulus operator between the months value and 2.
  • And like ours it is at that point that the percentLossByMonth value augments by 0.5.
  • Then similarly to ours, we return an array with months as the first value, and the various values of the man’s wealth with the new car price subtracted; the product of which is then rounded.

I think what I could have done was employ what a̵n̵a̵l̵ ̵r̵e̵t̵e̵n̵t̵i̵v̵e̵ ̵c̵a̵l̵c̵u̵l̵a̵t̵o̵r̵s̵ ̵w̵i̵t̵h̵ ̵h̵a̵i̵r̵ ̵c̵u̵t̵s̵ ̵&̵ ̵c̵i̵r̵c̵u̵l̵a̵t̵o̵r̵y̵ ̵s̵y̵s̵t̵e̵m̵s̵ good programmers refer to as the D.R.Y. Principle. I think of dirty things & loneliness immediately, but what it means is Don’t Repeat Yourself; as in, don’t rewrite code needlessly.

If we take a look at my solution, I repeat like three lines for the two cases in my switch statement:

switch(month%2 == 0) {case true:percentLossByMonth += 0.5;startPriceNew = startPriceNew — (startPriceNew * percentLossByMonth/100);dough -= (startPriceOld * percentLossByMonth/100);startPriceOld -= (startPriceOld * percentLossByMonth/100);break;default:startPriceNew = startPriceNew — (startPriceNew * percentLossByMonth/100);dough -= (startPriceOld * percentLossByMonth/100);startPriceOld -= (startPriceOld * percentLossByMonth/100);break;}

I could easily reformat my solution to look like this and still work.

Adrian’s Slightly Revised Solution:

function nbMonths(startPriceOld, startPriceNew, savingperMonth, percentLossByMonth){var dough = 0 + startPriceOld;let month = 0;   while(dough < startPriceNew) {   month++;   dough += savingperMonth;   switch(month%2 == 0) {     case true:      percentLossByMonth += 0.5;     break;     default:     break;  }startPriceNew = startPriceNew — (startPriceNew *      percentLossByMonth/100);dough -= (startPriceOld * percentLossByMonth/100);startPriceOld -= (startPriceOld * percentLossByMonth/100); }   if(dough >= startPriceNew){   return [month, Math.round(dough-startPriceNew)];   }}

We maintain the placement of actions happening after a judgement has been evaluated regarding our switch statement, so we put all the sensitive operations to help with finding the money amount after the switch statement. 🙂

And that solution also works.

Takeaways:

Buy me this take out…please?

You should strive to avoid repeating yourself. So often in coding, the difference between shippable code or at least functional code is a small typo or a stray character that aint supposed to be there. When you have one pristine instance of a particular block of code that is vital to the operation of what you are trying to do it is preferable to refer to it somehow either with a separate function or the importing of a module you’ve created elsewhere in your file structure, whatever…

  • Other than that…scary math problems can be okay. 🙂
  • Sometimes directions are wrong, we were told to expect a $1000 increase each month but our actual function gave us something different. Be sure to be very skeptical and thorough about what you’re handed to work with. Sometimes people will cut corners and leave just enough out of a description to put you on a wild goose chase. It’s like being given a video game without a controller…
…dammit

That’s kind of it for today. I may be able to come back and edit this post and add another problem, but I’ve got plans and rehearsals as well as a few projects to tinker with. Maybe I’ll make a special post about something related to a project I’m working on.

--

--