How to keep adding the base values in an array until condition is met?

How to keep adding the base values in an array until condition is met?
0

#1

Here’s my code:

let arr = [2, 5, 7, 9, 12, 14, 20];

let arr2 = [10, 50, 70, 80, 90, 120, 150];

let arr3 = [];

for (i = 0; i < arr.length; i++) {
  while (arr[i] < arr2[i]) {
    arr[i] += arr[i];
  }

  arr3.push(arr[i]);
}

console.log(arr3);

I need to add the base values in the first array elements as long as the first array element is smaller than the second array elements. But i end up doubling the values. I simply need to increment the values in the first array by the base values.

For example: I want:

2 + 2 = 4
4 + 2 = 6
6 + 2 = 8

as long as its less than 10.

so the result array should be: [8, 45, 63, 72, 84, 112, 140]

How to make that happen. I know I am missing something very simple here.


#2

How about something like:

const arr3 = arr.map((arrVal, i) => {
   return arr2[i] % arrVal === 0
     ? arr2[i] - arrVal 
     : Math.floor(arr2[i] / arrVal) * arrVal;
});

If there is no remainder when arr2 element is divided by arr element, then just subtract the arr element from arr2 element. Otherwise, you need to divide arr2 element by arr element ignoring any decimal and multiply by arr element.


#3

Isn’t there a simple way to do this? If I can just keep adding the initial value to the value until the last possible value is reached. What would be the simplest logic for it?


#4

It doesn’t work right for:

arr = [0.01, 0.05, 0.1, 0.25, 1, 5, 10, 20, 100];
arr2 = [1.01, 2.05, 3.1, 4.25, 90, 55, 20, 60, 100];


#5

I was not aware you could have non-integers. You did not mention that fact.

Should to make sure I am on the same page, what is the expected arr3 for the example you show above?


#6

Yes, my bad sorry. The result array should be the maximum value of each element less than the arr2 element. And the value should increment by itself. So if the value in arr is 10 and its corresponding arr2 value is 90, then the result value should be 80.


#7

What about the 100 which is present in both arrays? What should arr3 have for this element?


#8

First of all, your values are doubling because you’re reassigning them. You probably wanted to do something like:

for (let i = 0; i < arr.length; i++) {
  let counter = arr[i];
  while (counter < arr2[i]) {
    counter += arr[i];
  }

  arr3.push(counter);
}

Now, secondly, even if you would have done it, this wouldn’t work either :slight_smile: because you will still do the last addition after comparing values. You might do a little trick:

for (let i = 0; i < arr.length; i++) {
  let counter = arr[i];
  while (counter < arr2[i]) {
    counter += arr[i];
  }

  arr3.push(counter - arr[i]); //Here's the trick
}

or, something like this, which is slightly more readable:

for (let i = 0; i < arr.length; i++) {
  let counter = arr[i];
  while (counter + arr[i] < arr2[i]) {
    counter += arr[i];
  }

  arr3.push(counter);
}

Cheers


#9

If you return to this problem later on you may be interested to know that an operation on two lists like this is known as a zip or zipWith in other languages

It’s a nifty generalisation, but it’s not important to know or anything, just thought you might find it interesting

You can imagine it as taking a pair of lists, creating a list of pairs, and then reducing over the list of pairs

Edit: changed map to reduce, though more precisely it’s a scan


#10

@iiiked Your solution does not work, because it does not account for rounding errors in the calculations inherent to JavaScript and other languages.

For example, your code returns:

[ 1.0000000000000007, 2.000000000000001, 3.0000000000000013, 4, 89, 50, 10, 40, 100 ]

instead of:

[ 1, 2, 3, 4, 89, 50, 10, 40, 100 ]

#11

My goal was to only have a single for loop and avoid the while, because if an arr element was a very small number, that would be a lot of extra iterations. The following example only has two numbers, but imagine if it had a million numbers.

arr = [0.0000000001, 0.0000000005];
arr2 = [100000000.01, 100000000.05];

Here is my solution. It can probably be simplified, but I did feel like it today.

const splitNum = num => num.toString().split('.');

const powerCalc = num => {
  const [_, after = 0] = splitNum(num);
  return after ? Math.pow(10, after.length) : 1; // 1 prevents division by 0 in map return
};

const convertedNum = (num, maxPowerCalc) => {
  const [before, after = 0] = splitNum(num)
  return after
    ? maxPowerCalc * before + maxPowerCalc * Number('.' + after) 
    : before;
};

const arr3 = arr2.map((arr2Val, i) => {
  if (arr[i] === arr2Val) {
    return arr2Val;
  }
  else {
    // maxPowerCalc is neeed in case the two numbers of the same index have different # decimal places
    const maxPowerCalc = Math.max(powerCalc(arr2Val), powerCalc(arr[i]));
    const val1 = convertedNum(arr[i], maxPowerCalc);
    const val2 = convertedNum(arr2Val, maxPowerCalc);
    return (val2 - val1) / maxPowerCalc;
  }
});

#12

It’s interesting to me how much shorter your solution could be with fractional data types freely available

The edge case where these numbers become too large for this kind of treatment would be irrelevant with fractional numbers based on the bigint proposal


#13

@RandellDawson, first of all, this is @umairhp’s code. I just pointed out how to make his code work.
Secondly, it’s not the code that doesn’t account for rounding errors, it’s the way language works itself. It’s like saying that const add = (x, y) => x + y; is wrong, because add(.1, .2) doesn’t work :smile:

Now, if you want to discuss my code, discuss this ‘unreadable gibberish’:

const solveUmairhpProblem = (arr1, arr2) => arr1.map((n, i) => n >= arr2[i] ? n : arr2[i] % n ? arr2[i] - arr2[i] % n : arr2[i] - n);

#14

Not understanding your argument. The code you posted as a reply to the OP does not work in providing a correct solution regardless of why.it does not work.

My solution will not work with very large numbers which exceed JavaScripts max safe and min safe numbers. Without knowing how large or small or the exact wording of the problem the OP was given, we are all just guessing at the solution.


#15

Where did that come from?

Seems like you took a lot of offence where none was intended.

Alrighty then I guess I will take a look at your ‘unreadable gibberish’ if nobody else gets there first

const solveUmairhpProblem = (arr1, arr2) => arr1.map((n, i) => arr2[i] % n ? arr2[i] - arr2[i] % n : arr2[i] - n);

  • arr2[i] - arr2[i] can be simplified to 0 - is this what you want?
  • A map is the wrong abstraction when considering multiple arrays
  • Using the index with a map is bad form and should probably not have been allowed in the standard

If you intended to find arr2[i] - arr1[i] consider that arr2[i] - arr1[i] doesn’t work for 10 and 0.3, and also consider the fact that you have n already and don’t need to index arr1


#16

I might have created some confusion given how many posts are here. I was replying to ‘doubling’ problem in the first post. And by saying ‘to make it work’ I clearly meant this “I simply need to increment the values in the first array by the base values.”


#17

Com’on @gebulmer, no one makes an offensive post with smiles :slight_smile:
The ‘unreadable gibberish’ was addressed to @RandellDawson only, he knows why

Thanks for the feedback.

  1. I’m not sure JS would do negation before modulo, so arr2[i] - arr2[i] unfortunately is not a case here.
  2. What’s wrong about mapping item of the array to the item of another array?
  3. What??? How come? :slight_smile:

#18

Actually, I do not know why. That was the first time I had seen that code. If this was something just for me, send me a private message instead.


#19

for (1) I misread the line as it was quite dense, my bad! :sweat_smile:

It’s not clear to me what (float) % (float) is meant to mean anyway, but JS is weird!

(2) and (3) are really down to the same thing, interpretation of what a map should be

In many languages, especially functional ones, (of which javascript is one in disguise!) a map refers to using a pure function that uses only the element of a list it’s currently operating on with no side effects

It makes reasoning about things that happen to large lists very easy, all you need to do is look at the function, think about what it does to a single (number/list/whatever) and you know what the final list looks like.

There’s also room with pure maps (of which JS can’t guarantee :frowning: )for some really cool optimisations where the array can be operated on in parallel and things like that

reduce or fold as it’s known elsewhere is an incredible tool - I won’t spoil how but it’s more general than the rest of these, and the rest of these can be implemented with it (and it’s still pure)

forEach is the abstraction that’s like map that’s associated with impure side effects such as mutation

The following should be in JS and aren’t:

zip is the abstraction that takes two lists and combines them to a list of pairs which can later be merged together

zipWith is the abstraction that takes two lists, examines the elements of each list together and calls a function of two variables on their elements

Note that zipWith is like a map over two arrays at once with a function of two variables


#20

I’m not sure I got the part about map side effects and unpurity…

const arr1 = [1, 2, 3];
const arr2 = [5, 6, 7];
const mapIt = (arr1, arr2) => arr1.map((n, i) => n + arr2[i]);
const arr3 = mapIt(arr1, arr2);
arr3.pop();
console.log(arr1);
console.log(arr2);
console.log(arr3);
console.log(arr1 === arr3);
console.log(arr2 === arr3);