Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

JavaScript

Random quote generator - not returning duplicate quote

I'm writing a random quote generator and I'm stuck on ensuring that my getRandomQuote function doesn't return a duplicate quote until all the quotes have been returned once.

Any hints are appreciated. Thanks!

// event listener to respond to clicks on the page
// when user clicks anywhere on the page, the "makeQuote" function is called
document.getElementById('loadQuote').addEventListener("click", printQuote, false);

// get random quote function that selects a random quote object from the quotes array
// and returns the randomly selected quote object

// variable that will hold viewed quotes
var viewedQuotes = [];

function getRandomQuote() {
    var theQuote = Math.floor(Math.random() * (quotes.length));
    return quotes[theQuote];
}

// printQuote calls the getRandomQuote function and stores the returned quote object in a variable
// printQuote constructs a string using the different properties of the quote object using the following
// HTML template: <p class="quote"> [quote here] </p> <p class="source"> [source here]
// <span class="citation"> [citation here] </span> <span class="year"> [year here] </span> </p>
// printQuote doesn't add a <span class="citation"> for a missing citation or a
// <span class="year"> if the year property is missing
// printQuote displays the final HTML string to the page. You can use the following
// JS snippet to accomplish that: document.getElementById('quote-box').innerHTML
function printQuote() {
  var selectedRandomQuote = getRandomQuote();
  var html = '<p class="quote">' + selectedRandomQuote.quote + '</p>'
  + '<p class="source">' + selectedRandomQuote.source +
  '<span class="citation">' + selectedRandomQuote.citation + '</span>'
  + '<span class="year">' + selectedRandomQuote.year + '</span>' + '<span class="category">'
  + selectedRandomQuote.category + '</span>' + '</p>';
  document.getElementById('quote-box').innerHTML = html;
}

var quotes = [
  {
    quote: 'The journey of a thousand miles begins with one step.',
    source: 'Lao Tzu',
    citation: 'Wiktionary',
    year: 'c 551 bc - c 479 bc',
    category: "Inspiration"
  },
  {
    quote: 'He who knows best knows how little he knows.',
    source: 'Thomas Jefferson',
    citation: 'Monticello',
    year: '1812',
    category: 'Inspiration'
  }
];

2 Answers

One way of doing this is to modify your getRandomQuote function to run through a few more steps before returning the randomly generated quote object. Without giving away the code, you can do the following:

  • splice the object from the quote array and store it in a variable.
  • push the spliced object into the viewQuotes array.
  • you will then need an "if" statement to check if first array is now empty(all quotes have been used/displayed).
  • if first array is empty, change it back to original state, and set viewQuotes back to an empty array

You will need to add these steps in first before "return quotes[theQuote]". Hope these hints help.

Thanks for the response Huy Bui. Nicely detailed.

I've incorporated your feedback but am running into an error in the console: script.js:112 Uncaught TypeError: Cannot read property 'quote' of undefined

My modified code below:

// get random quote function that selects a random quote object from the quotes array
// and returns the randomly selected quote object
function getRandomQuote() {
    var theQuote = Math.floor(Math.random() * quotes.length);
    var splicedQuote = quotes.splice(theQuote, 1);
    viewedQuotes.push(splicedQuote);
    if (quotes.length == 0) {
      quotes.push(viewedQuotes);
      viewedQuotes = [];
    }
    return quotes[theQuote];
}

Line 112 is pointing to my printQuote function:

function printQuote() {
  var selectedRandomQuote = getRandomQuote();
  var html = '<p class="quote">' + selectedRandomQuote.quote + '</p>'
  + '<p class="source">' + selectedRandomQuote.source +
  '<span class="citation">' + selectedRandomQuote.citation + '</span>'
  + '<span class="year">' + selectedRandomQuote.year + '</span>' + '<span class="category">'
  + selectedRandomQuote.category + '</span>' + '</p>';
  document.getElementById('quote-box').innerHTML = html;
  randColor();
}

Where line 112 is this line:

  var html = '<p class="quote">' + selectedRandomQuote.quote + '</p>'

After a while of displaying quotes, the web browser displays the page with everything shown as "undefined" (quotes, source).

You just need to add a [0] to your spliceQuote line. The reason for this is that the .splice method returns your single object in an array, so you just need to access the first item in that array which is at index 0.

var splicedQuote = quotes.splice(theQuote, 1)[0];  // add [0]

Also to set your "quotes" array back to the original state when all the quotes have been used, you can just set it to be viewedQuotes instead of pushing the array itself. Example:

if (quotes.length == 0) {
      quotes = viewedQuotes;
      viewedQuotes = [];
}

Huy Bui I'm still getting the same error and duplicate quotes to appear.

Here's my code:

// get random quote function that selects a random quote object from the quotes array
// and returns the randomly selected quote object
function getRandomQuote() {
    var theQuote = Math.floor(Math.random() * quotes.length);
    var splicedQuote = quotes.splice(theQuote, 1)[0];
    viewedQuotes.push(splicedQuote);
    if (quotes.length == 0) {
      quotes = viewedQuotes;
      viewedQuotes = [];
    }
    return quotes[theQuote];
}

The printQuote function is still the same.

@Alborz M. you're almost there! just need to change one small detail in your return statement. Since you've already saved the spliced object into the "splicedQuote" variable, you want to return that instead. Change it to:

// get random quote function that selects a random quote object from the quotes array
// and returns the randomly selected quote object
function getRandomQuote() {
    var theQuote = Math.floor(Math.random() * quotes.length);
    var splicedQuote = quotes.splice(theQuote, 1)[0];
    viewedQuotes.push(splicedQuote);
    if (quotes.length == 0) {
      quotes = viewedQuotes;
      viewedQuotes = [];
    }
    return splicedQuote;  // changed "quotes[theQuote]" to "splicedQuote"
}

here is a simple jsfiddle example.

Huy Bui - I had to return the spliced variable! That did the trick! Thanks!

Steven Parker
Steven Parker
243,318 Points

I see you have already reserved a viewedQuotes list. So now each time you pick a random quote, remove it from the main list and add it to the "viewed" list. Then, before you pick one, if the main list is empty move everything back to the main list.

:point_right: Here's one way to code it:

function getRandomQuote() {
  if (quotes.length == 0)                                       // if empty, reload the main list
    quotes = viewedQuotes.splice(0, viewedQuotes.length);       // (and empty viewed list)
  var theQuote = Math.floor(Math.random() * quotes.length);     // pick a quote at random
  var splicedQuote = quotes.splice(theQuote, 1)[0];             // take it out of the main list
  viewedQuotes.push(splicedQuote);                              // now add it to the "viewed" list
  return splicedQuote;                                          // return the chosen quote
}

Note that you can still get the same choice twice in a row if the first pick from a fresh list happens to be the same as the last one from the old list.
:sparkles:

Thanks for the response Steven Parker. If you see my above comments I'm getting an error in the console and still getting duplicates displayed. Thanks!

Steven Parker
Steven Parker
243,318 Points

I notice you deviated from my original suggestion a bit. In particular, you end the getRandomQuote function with this line:

    return quotes[theQuote];

But after performing the splice on the quotes array, that index is not valid anymore, and the selected quote has already been removed from the array.

:point_right: The getRandomQuote function should return this way instead:

    return splicedQuote;

:sparkles:

Steven Parker
Steven Parker
243,318 Points

Not that the points are a big deal, but why the heck would anyone downvote the reply that was correct the first time? :anguished: