Last active
December 2, 2020 00:02
-
-
Save ricealexander/4157c29b697ef37a0db1a047946a4b80 to your computer and use it in GitHub Desktop.
Gets an item from an array by its index. For indexes outside of the array, it loops the index.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function getItem (array, index) { | |
if (typeof index !== 'number' || Number.isNaN(index)) { | |
throw new TypeError(`Expected index to be a Number. Instead got "${index}"`) | |
} | |
if (Math.abs(index) === Infinity) { | |
throw new ReferenceError(`Cannot access item at index "${index}"`) | |
} | |
const {length} = array | |
let wrappedIndex = index | |
// wrappedIndex is a copy of the provided index | |
// first handle the lower-bounds and then handle the upper-bounds | |
// this is because lower bounds formula returns array.length when index is -array.length, | |
// which causes a ReferenceError | |
if (wrappedIndex < 0) { | |
wrappedIndex = length + (wrappedIndex % length) | |
} | |
if (wrappedIndex >= length) { | |
wrappedIndex = wrappedIndex % length | |
} | |
return array[wrappedIndex] | |
} | |
const fruits = [ | |
'apple', // 0 | |
'banana', // 1 | |
'cherry', // 2 | |
'dragonfruit', // 3 | |
'elderberry', // 4 | |
'fig', // 5 | |
'grape', // 6 | |
'honeydew', // 7 | |
] // length: 8 | |
getItem(fruits, 0) // apple | |
getItem(fruits, 2) // cherry | |
getItem(fruits, 8) // apple | |
getItem(fruits, -2) // grape | |
getItem(fruits, -8) // apple | |
getItem(fruits, 15) // apple | |
getItem(fruits, -17) // apple | |
// The original use-case I had from this was for an image slider. | |
// With sliders, a wrap around effect is often used so the slider loops across its slides infinitely. | |
// To code this logic, there needs to be range handling for the index | |
// const nextSlide = this.slides[currentSlide + 1] ?? this.slides[0] | |
// const previousSlide = this.slides[currentSlide - 1] ?? this.slides[this.slides.length - 1] | |
// | |
// That's simple enough, but what if there needs to be a wider gap? Then you start writing logic like: | |
// const laterSlide = this.slides[currentSlide + 3] ?? this.slides[(currentSlide + 3) % this.slides.length] | |
// const earlierSlide = this.slides[currentSlide - 3] ?? this.slides[this.slides.length + ((currentSlide - 3) % this.slides.length)] | |
// | |
// In my case, the client had many slides. Pressing the arrows or clicking the icon would iterate to the | |
// previous or next slide, but the client wanted a way to navigate faster with a shortcut. | |
// The proposal was that Shift-clicking or Shift-arrowing would jump 3 slides instead. | |
// | |
// I talked the client out of it, but came up with this helper and have used it any other time I've needed to wrap indexes. | |
// const nextSlide = getItem(this.slides, currentSlide + 1) | |
// const previousSlide = getItem(this.slides, currentSlide - 1) | |
// const laterSlide = getItem(this.slides, currentSlide + 3) | |
// const earlierSlide = getItem(this.slides, currentSlide - 3) | |
// | |
// Regardless of the use-case, no worrying is needed regarding how the slides will wrap. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment