Stack Exchange
How to get numbers to cycle around specific range in JavaScript?
I have this which isn't working, for cycling like 1 2 3 4 5 6 7 8 9 1 2 3 4 ..., cycling between 1 and 9.


const cycle = integers => {
  let i = 0
  let x = integers[i++]
  while (i < integers.length) {
    let y = integers[i++]
    const s = x + y
    const remainder = (s % 9) + 1
    x = remainder
  }
  return (x - 1) % 9
}

logCycle([ 9, 6 ])
logCycle([ 9, 9 ])
logCycle([ 9, 9, 9, 9, 9 ])
logCycle([ 9, 129, 993, 91, 9 ])
logCycle([ 9, 10 ])

function logCycle(i) {
  log('cycle', i, cycle(i))
}

function log(t, i, o) {
  console.log(`${t}(${JSON.stringify(i)}) => ${o}`)
}



Notice how at cycle([9, 9]) it is returning 0, and other places it breaks down too. The cycle function takes an array of integers, of arbitrary length. So the array may have 1000000 items, or just 2.
I expect:

cycle([9, 9]) => 9
cycle([9, 10]) => 1

How do I get this to work properly? And how do I get it to work for any pair of min/max numbers, like if I wanted to cycle between (and including) 5 and 13, or 79 and 5337, how would I make it generic like that?
The cycle function is summing and doing modulus on the result, to have it "cycle" ideally within that range. It isn't iterating through the cycle, it is "summing through the cycle", like a clock.
Also looking to keep the solution non-recursive, as the number of elements in array might be large.

Array will always be of length = 2?

You talk about working with any pair of min/max numbers, but then talk about how array could get large. Is array supposed to be [min, max] or something else?

I need it to work for arrays larger than just 2, how does it work for arrays of arbitrary size, within that range. The numbers I gave are for boundaries, not for the array values. An array might look like [1, 811, 29317, 391, 21], then it should cycle through for example 5..13.

@Lance - probably worth editing that into the question. Without named parameters, it's a guessing or reading/comprehension game that's getting in the way of both your answer and a clear question.

I did edit it, what parameters do I need to name?

@lance - The code takes input. The only clue we have what form that input takes is to read the comments. (other than spending time to understand it) It still remains entirely unclear why the input would ever include more than 2 numbers - the lower and upper bounds. Given the context given in the question, it's my assertion that the following input appears ridiculous: logCycle([ 9, 129, 993, 91, 9 ]) :shrug:

Please update your question and include more examples of input/output (what you want to pass, what you expect to get back). Your question, as it is, isn't very clear.

Apparently I misunderstood some parts of the question at first. After having read even further comments it seems to me that

the numbers to step through can be any previously defined sequence, like, from 1 to 9, from 5 to 13 or from 79 to 5337
the total number of steps for going (repeatedly) through the above sequence is given by the sum of all the arguments to the cycle function.

For these requirements the following should work:


function defCycle(m,n){
    const base=n-m+1,off=m-1;  // base: sequence-length,  off: offset of sequence
    return arr=>(arr.reduce((a,c)=>a+c)%base||base)+off;
};

// run the function on a collection of test data:
const ntest=1000000;
[[1,9],[5,13],[79,5337]]
.forEach(([m,n],cycle)=>{
  console.log(`Sequence from ${m} to ${n}:`);
  cycle=defCycle(m,n);
  [[ 9, 6 ],[ 9, 9 ],[ 9, 9, 9, 9, 9 ],[ 9, 129, 993, 91, 9 ],[ 9, 10 ],[1, 811, 29317, 391, 21],[ntest*(ntest+1)/2]]
  .forEach(arr=>console.log(`cycle(${JSON.stringify(arr)}) => ${cycle(arr)}`));
  console.log(`cycle([1,2,3,...,${ntest}]) => ${cycle([...Array(ntest)].map((_,i)=>i+1))}`);
});



The last two of the test cases should produce the exact same result: going through the sum of a sequence array from 1 to 1000000 ( = (1000000*1000001)/2) should result in 1 (for the sequence: "1...9").
The action within the defCycle() function happens in these two lines:

base=n-m+1,off=m-1;
base: the number of sequence elements
off: the offset of the sequence
example (sequence from 5 to 11): m=5,n=11 => base=7,off=4
return arr=>(arr.reduce((a,c)=>a+c)%base||base)+off;
this returns a singe-argument-function based on the previously calculated base and off. The calculation consists of the two expressions:

[shift] = (arr.reduce((a,c)=>a+c) sums up all the elements of the arr passed as argument - and
[shift]%base||base calculates the actual result: the modulo(base) of the total [shift]. Whenever the result ==0 it is replaced with the base value.




I need it to work for arrays larger than just 2, how does it work for arrays of arbitrary size, within that range, I don't think you fully understood the question. The numbers I gave are for boundaries, not for the array values. An array might look like [1, 811, 29317, 391, 21], then it should cycle through for example 5..13.

@Lance You need to clarify your question. You've only provided examples with arrays of size two and your explanation of your expected input/output is vauge.

I did clarify the question, what else needs clarification?

Your example is hardcoded to 9, how do I use the same inputs, but cycle around/between 5 and 13 (arbitrary start and end to the cycle)?

Do you mean that the sequence to go through can be different for each cycle()-call? So it could be the 9 numbers 5,6,7,8,9,10,11,12,13 instead of my assumed 9 numbers 1,2,3,4,5,6,7,8,9? How do you want to specify this sequence?  By giving an offset as a first argument? Please specify in terms of an example.

@Lance I think the fact that no one who has read your question understands what you're asking for is a pretty good sign it isn't clear.

If you only need the result (no need to perform other operations while cycling), then you can calculate it fairly easily using base conversion.


function getResult(base, num) {
  const str = (parseInt((num-1).toString(base)) + 1).toString()
  return str.charAt(str.length - 1)
}

console.log( getResult(9, 6) ) // 6

console.log( getResult(9, 9) ) // 9

console.log( getResult(9, 10) ) // 1




I need it to work for arrays larger than just 2, how does it work for arrays of arbitrary size, within that range, I don't think you fully understood the question. The numbers I gave are for boundaries, not for the array values. An array might look like [1, 811, 29317, 391, 21], then it should cycle through for example 5..13.

@Lance Why would the input array [1, 811, 29317, 391, 21] cycle through 5 to 13?

Does it matter why? It's the problem I am trying to solve.

This is too weird, I'm out of here.
