0

I'm very new to coding (wrote my first line of html like 3 weeks ago) and for my first own project, I wanted to create a small gallery that displays random images.

First I only managed to display the same image every time the function was called, then I managed to make it so that the images were rolled separately, but there could have been repetition. Finally, with some help, I got to the current version:

//Images array and global vars
let theImages = [];
for (let i = 0; i <= 25; i++) {
    theImages[i] = "stevejohnson_" + i + ".jpg";
}

let l = theImages.length;
let generated = [];


//Function to pick a random image and display it
function showImage() {
    let whichImage;
    let newImage;
    do {
        whichImage = Math.round(Math.random() * (l - 1));
        newImage = true;
        for (let i = 0; i < generated.length; i++) {
            if (generated[i] === whichImage) {
                newImage = false;
                break;
            }
        }
        if (newImage) {
            console.log("new: " + whichImage);
            generated.push(whichImage);
        } else {
            console.log("already was generated: " + whichImage); //just to see if it works
        }
    } while (!newImage);
    document.write('<img src="' + theImages[whichImage] + '" alt="A random painting of Steve Johnson"' + '>');
}

Now while this works well if I have 200 images and want to display 5, if I want to display 9 images and I only have 10, it can easily end up like this:

new: 8
paint.js:25 new: 0
paint.js:25 new: 7
paint.js:25 new: 5
paint.js:28 already generated: 8
paint.js:28 already generated: 5
paint.js:25 new: 3
paint.js:25 new: 9
paint.js:28 already generated: 8
paint.js:28 already generated: 3
paint.js:25 new: 6
paint.js:28 already generated: 7
paint.js:28 already generated: 3
paint.js:28 already generated: 6
paint.js:28 already generated: 5
paint.js:25 new: 1
paint.js:28 already generated: 6
paint.js:28 already generated: 5
paint.js:28 already generated: 6
paint.js:28 already generated: 7
paint.js:28 already generated: 3
paint.js:25 new: 2

Which is not optimal.

How could I change the function so that it removes the picked images from the array instead of rerolling whenit picks an already used one? My idea to fix this was to splice the "used image" from the array, but anything I try ends up breaking something. Thanks in advance for any help!

Tom
  • 1
  • 1

1 Answers1

0

Yeah, Array.prototype.splice is the way to go. It will remove an item from the original array so that it can't be referenced again. However, since arrays are references, it will also remove that item until the script is restarted.

To prevent the original array from being lost to the mists of time, it can be cloned. A shallow clone is fine for this case, as the process doesn't mess with any of the things in the array.

A lot of ink has been spilled on cloning things in javascript, so I won't do that here.

This following example just gets random numbers from an array, but hopefully it can help you figure out what you need.

function getRandomPickList(list, length) {
  // Clone the original array as the function is going to destroy an array.
  const clone = [...list];
  
  // Just a spot to keep the picked values in.
  const randomPick = [];
  
  // Execute this loop until the randomPick array reaches the desired length
  // or until the clone runs out of items.
  while (randomPick.length < length && clone.length) {
    // Add an item to the randomPick array.
    randomPick.push(
      // Remove an item from the input array.
      clone.splice(
        // Normal way to get random index from an array.
        Math.floor(Math.random() * clone.length),
        1
      )[0]
    );
  }
  return randomPick;
}

const myNumbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
const newNumbersLength = 9;

console.log(getRandomPickList(myNumbers, newNumbersLength));

Incidentally, Math.round shouldn't be used to get a random item from an array, as it will give you an out of bound value if the Math.random() returns a value greater than 1 - (1 / (l * 2)). For a trivial example, consider trying to get a value from an array with length 1. Math.round(Math.random() * 1) has a fifty percent chance of returning 1, but the only valid index is 0.

Charlie Bamford
  • 1,268
  • 5
  • 18
  • I'm using javascript's spread operator (...) to turn the array into all it's values, then putting those values into another array. Cloning things starts off easy, but there are a lot of considerations when the data gets more complicated. – Charlie Bamford Jun 17 '21 at 23:38
  • I don't know much about cloning yet, I guess I have my program for tonight :) I call the script like this, to have each image in their own box:
    doesn't this restart the script every time I call it?
    – Tom Jun 17 '21 at 23:38
  • No, you'd need to refresh the page to restart the script. To be clear, your `showImage` function doesn't currently change the original array. If you use [Array.prototype.splice](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice), however, you would. – Charlie Bamford Jun 17 '21 at 23:41
  • That sounds good, as I have a button on the bottom that reloads the page and jumps to the top of the section. I'll give it a shot once I understand cloning better. Thank you! – Tom Jun 17 '21 at 23:45