CoDEVIANT #4 (3/24/19)

Adrian Rosales
Mar 24 · 11 min read

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:

*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:

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 😉 )

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…

…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.

Adrian Rosales

Written by

is a lyric operatic baritone and a full-stack (mostly front-end) web developer and budding UX person. He’s looking for shows to do and projects to code.