How to access array in circular manner in JavaScript
Asked Answered
D

9

23

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?

Detrude answered 5/7, 2013 at 7:15 Comment(0)
C
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(","));
Camphorate answered 5/7, 2013 at 7:18 Comment(0)
K
53

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
Kanishakanji answered 29/1, 2019 at 18:5 Comment(5)
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].Gelhar
I don't get it. (Negative number as array length?) Could you rephrase what you are trying to point out?Kanishakanji
Why not just let val = arr[Math.abs(i % n)]; ?Musketry
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.Kanishakanji
@wwgoncalves, you made my day. People in the comments below are giving funny answers, but yours is the simplest and most effective.Larock
C
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(","));
Camphorate answered 5/7, 2013 at 7:18 Comment(0)
A
11

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('****')
}
Amice answered 3/9, 2014 at 17:12 Comment(0)
D
3

how about this one-liner I made ?

var nextItem = (list.indexOf(currentItem) < list.length - 1)
                        ? list[list.indexOf(currentItem) + 1] : list[0];
Decorate answered 31/3, 2016 at 13:57 Comment(0)
S
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(','));
}
Steady answered 5/7, 2013 at 7:23 Comment(0)
V
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());});
Valine answered 22/1, 2019 at 14:8 Comment(3)
Do you think Circular List is a way to solve this question? #62238842Counterclaim
@JoãoRamires You should use getTime/setTime for your own question. See my answer there #62238842Irk
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]?Gelhar
M
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));
}
Musketry answered 5/2, 2021 at 11:23 Comment(0)
S
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; }
Septuplet answered 6/7, 2022 at 9:32 Comment(0)
S
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);
}
Sculptor answered 7/7, 2022 at 6:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.