21

I have an array like [A,B,C,D]. I want to access that array within a for loop like as

var arr = [A,B,C,D];

var len = arr.length;
for(var i = 0; i<len; i++){
    0 - A,B,C
    1 - B,C,D
    2 - C,D,A
    3 - D,A,B
}

I want to access that like in JavaScript, any ideas?

Debug Diva
  • 26,058
  • 13
  • 70
  • 123
Anshul
  • 9,312
  • 11
  • 57
  • 74

9 Answers9

48

Answering to the main question, someone can access an array in a circular manner using modular arithmetic. That can be achieved in JavaScript with the modulus operator (%) and a workaround.

Given an array arr of a length n and a value val stored in it that will be obtained through an access index i, the circular manner, and safer way, to access the array, disregarding the value and sign of i, would be:

let val = arr[(i % n + n) % n];

This little trick is necessary -- someone can not use the modulus result straightforwardly -- because JavaScript always evaluates a modulus operation as the remainder of the division between dividend (the first operand) and divisor (the second operand) disconsidering their signs but assigning to the remainder the sign of the dividend. That behavior does not always result in the desired "wrap around" effect of the modular arithmetic and could result in a wrong access of a negative position of the array.

References for more information:

  1. https://www.khanacademy.org/computing/computer-science/cryptography/modarithmetic/a/what-is-modular-arithmetic
  2. https://en.wikipedia.org/wiki/Modular_arithmetic
  3. https://en.wikipedia.org/wiki/Modulo_operation
  4. https://dev.to/maurobringolf/a-neat-trick-to-compute-modulo-of-negative-numbers-111e
wwgoncalves
  • 685
  • 1
  • 5
  • 6
  • OK. But this does not provide a wrap around slice of the source array. It also fails with negative numbers; e.g. `[1,2,3]` `start = 1`,` len = -3` => `[2,1,3]`. – Garrett Feb 02 '21 at 02:40
  • 3
    I don't get it. (Negative number as array length?) Could you rephrase what you are trying to point out? – wwgoncalves Feb 03 '21 at 21:06
  • Why not just `let val = arr[Math.abs(i % n)]; ` ? – Mkdgs Feb 05 '21 at 10:43
  • 1
    Unfortunately that won't work for negative values of `i`. The result -- remainder of the division -- will be different from the result expected from modular arithmetic. For example in JS `(-1 % 10 + 10) % 10` evaluates to `9`, but `Math.abs(-1 % 10)` evaluates to `1`. – wwgoncalves Feb 10 '21 at 19:06
  • @wwgoncalves, you made my day. People in the comments below are giving funny answers, but yours is the simplest and most effective. – Alexander Abashkin Jun 17 '23 at 04:20
15

Try this:

var arr = ["A","B","C","D"];
for (var i=0, len=arr.length; i<len; i++) {
    alert(arr.slice(0, 3).join(","));
    arr.push(arr.shift());
}

Without mutating the array, it would be

for (var i=0, len=arr.length; i<len; i++) {
    var str = arr[i];
    for (var j=1; j<3; j++)
        str += ","+arr[(i+j)%len]; // you could push to an array as well
    alert(str);
}
// or
for (var i=0, len=arr.length; i<len; i++)
    alert(arr.slice(i, i+3).concat(arr.slice(0, Math.max(i+3-len, 0)).join(","));
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
10

Simply using modulus operator you can access array in circular manner.

var arr = ['A', 'B', 'C', 'D'];

for (var i = 0, len = arr.length; i < len; i++) {
  for (var j = 0; j < 3; j++) {
    console.log(arr[(i + j) % len])
  }
  console.log('****')
}
Ivar
  • 6,138
  • 12
  • 49
  • 61
user1948585
  • 170
  • 1
  • 7
3

how about this one-liner I made ?

var nextItem = (list.indexOf(currentItem) < list.length - 1)
                        ? list[list.indexOf(currentItem) + 1] : list[0];
DanteTheSmith
  • 2,937
  • 1
  • 16
  • 31
2
for (var i = 0; i < arr.length; i++) {
    var subarr = [];
    for (var j = 0; j < 3; j++) {
        subarr.push(arr[(i+j) % arr.length]);
    }
    console.log(i + " - " + subarr.join(','));
}
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
Barmar
  • 741,623
  • 53
  • 500
  • 612
0

One line solution for "in place" circular shift:

const arr = ["A","B","C","D"];
arr.forEach((x,i,t) => {console.log(i,t); t.push(t.shift());});
console.log("end of cycle", arr); // control: cycled back to the original

logs:

0 Array ["A", "B", "C", "D"]
1 Array ["B", "C", "D", "A"]
2 Array ["C", "D", "A", "B"]
3 Array ["D", "A", "B", "C"]
"end of cycle" Array ["A", "B", "C", "D"]

If you want only the first 3 items, use:

arr.forEach((x,i,t) => {console.log(i,t.slice(0, 3)); t.push(t.shift());});
allez l'OM
  • 547
  • 4
  • 13
  • Do you think Circular List is a way to solve this question? https://stackoverflow.com/questions/62238342/how-to-allow-a-time-interval-with-bootstrap4-date-time-picker – João Ramires Jun 07 '20 at 00:15
  • @JoãoRamires You should use getTime/setTime for your own question. See my answer there https://stackoverflow.com/questions/62238342/how-to-allow-a-time-interval-with-bootstrap4-date-time-picker – allez l'OM Jun 08 '20 at 08:40
  • That approach creates an array for each shift. What if there was a function like slice that wraps around and takes a length, e.g. `array.cirSlice(start, len)`. `[1,3,4].cirSlice(3,5)` results `[4,1,3,4,1]`? – Garrett Mar 03 '21 at 16:21
0

Another solutions:

    var arr = ['A','B','C','D'];
    var nextVal = function (arr) {        
        return arr[( ( ( nextVal.counter < ( arr.length - 1 ) ) ? ++nextVal.counter : nextVal.counter=0  )   )];
    };

    for(var i=0;i<arr.length;i++){
        console.log(nextVal(arr)+','+nextVal(arr)+','+nextVal(arr));
    }

And based on Modulo :

var arr = ['A','B','C','D'];
var len = arr.length;

var nextVal = function (arr, dir = 1) { 
        if ( dir < 0 ) { nextVal.counter--;}
        let i = (nextVal.counter % len + len) % len;  
        if ( dir > 0 ) { nextVal.counter++; }
        return arr[i];
};

nextVal.counter=0;
for(var i=0;i<arr.length;i++){
    console.log(nextVal(arr)+','+nextVal(arr)+','+nextVal(arr));
}

// in reverse 
console.log('-------------------');
nextVal.counter=0;
for(var i=0; i<10; i++) {
    console.log(nextVal(arr, -1)+','+nextVal(arr, -1)+','+nextVal(arr, -1));
}
Mkdgs
  • 167
  • 2
  • 11
0

You could get the sliced part from index and the rest of slicing from start, if necessary.

This appraoch does not mutate the array.

const
    getCircular = (array, size) => array.map((_, i, a) => [
        ...a.slice(i, i + size),
        ...a.slice(0, i + size < a.length ? 0 : i + size - array.length)
    ]);


console.log(getCircular(['A', 'B', 'C', 'D'], 3).map(a => a.join('')));
console.log(getCircular(['A', 'B', 'C', 'D'], 5).map(a => a.join('')));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
0

We can simply achieve this by using Array.splice() method along with the Destructuring assignment

Live Demo :

// Input array.
let arr = ['A', 'B', 'C', 'D'];

// Finding the length of an array.
const len = arr.length;

// Iterattion based on array length. 
for(let i = 0; i < len; i++) {
    const splittedArr = arr.splice(0, 3);
    arr.push(splittedArr[0]);
    arr = [splittedArr[1], splittedArr[2], ...arr]
    console.log(splittedArr);
}
Debug Diva
  • 26,058
  • 13
  • 70
  • 123