3

I am making a user testimonial for my website. I am displaying 5 user testimonials on the homepage and these get pulled @random from an array when the user loads the webpage. After the script takes 5 testimonials at random, it injects that into my HTML file.

This works all great, but I also want each value to be checked to make sure this value is not pulled already for one of the other 4 testimonials.

Then if 2 of the values are the same i would like the script to pull a other value of the array. Then it once again needs to check if the value that was pulled is not used in a other testimonial.

I am trying to find a way to do this as efficient as possible. I know how to do this if I am only pulling 2 values, but right now I am pulling 5 values and each needs to be checked with any other value that was pulled.

var user = [
    {
        name: 'Tom Levis',
        body: 'Toms text'
    },
    {
        name: 'Some Random Guy',
        body: 'Some guys text'
    },
    {
        name: 'Peter',
        body: 'Peters text'
    }
];

var random1 = user[~~(Math.random() * user.length)];
var random2 = user[~~(Math.random() * user.length)];
var random3 = user[~~(Math.random() * user.length)];
var random4 = user[~~(Math.random() * user.length)];
var random5 = user[~~(Math.random() * user.length)];

document.getElementById('testimonial-1').innerHTML = random1.name + '<br>' + '<p id="testimonial-text">' + random1.body + '</p>';
document.getElementById('testimonial-2').innerHTML = random2.name + '<br>' + '<p id="testimonial-text">' + random2.body + '</p>';
document.getElementById('testimonial-3').innerHTML = random3.name + '<br>' + '<p id="testimonial-text">' + random3.body + '</p>';
document.getElementById('testimonial-4').innerHTML = random4.name + '<br>' + '<p id="testimonial-text">' + random4.body + '</p>';
document.getElementById('testimonial-5').innerHTML = random5.name + '<br>' + '<p id="testimonial-text">' + random5.body + '</p>';

Eventually what should happen is that there are 5 different values pulled from the array. None of the results should contain the same values.

double-beep
  • 5,031
  • 17
  • 33
  • 41
DevDokus
  • 149
  • 8
  • 1
    I assume the `user` array has a length greater than `5` outside the example you made, is that right? – Shidersz Mar 27 '19 at 03:55
  • Yes the array in teh actual code is much much longer. Eventually it needs to pull the info from the database so i dont have to manually enter the values. BUt i think the code of kilkfoe is exactly what i need :) Thanks for your time buddy. – DevDokus Mar 27 '19 at 04:11
  • Out of interest, may I ask what does double `~~` do before parentheses? – Masoud Keshavarz Mar 27 '19 at 04:47
  • 5
    Please mark one of the answers as the solution (to signify the question is solved, and to make the answer easier to find for future readers), instead of adding "SOLVED" to the title. – JBallin Mar 27 '19 at 04:48
  • 1
    @MasoudKeshavarz It rounds the number (see [this answer](https://stackoverflow.com/a/4055675/8376184)) by converting to binary format which only allows integers. – FZs Mar 27 '19 at 05:27

3 Answers3

2

I think the most efficient way of doing this is just removing the testimonials that have been chosen from the pool of available choices.

const testimonials = [
    {
        name: 'Tom Levis',
        body: 'Toms text'
    },
    {
        name: 'Some Random Guy',
        body: 'Some guys text'
    },
    {
        name: 'Peter',
        body: 'Peters text'
    }
]

function getTestimonial() {
    let index = ~~(Math.random() * testimonials.length)
    let val = testimonials[index]
    testimonials.splice(index, 1)
    return val
}


var random1 = getTestimonial();
var random2 = getTestimonial();
var random3 = getTestimonial();
var random4 = getTestimonial();
var random5 = getTestimonial();

console.log(random1)
console.log(random2)
console.log(random3)
console.log(random4)
console.log(random5)

If you want to avoid mutating the data, you can just add id property to each entry and check the random testimonials's id against an array of already selected testimonials and their corresponding ids.

const data = [
    {
        id: 1,
        name: 'Tom Levis',
        body: 'Toms text'
    },
    {
        id: 2,
        name: 'Some Random Guy',
        body: 'Some guys text'
    },
    {
        id: 3,
        name: 'Peter',
        body: 'Peters text'
    }
]

const selected = []

function getTestimonials2(testimonials) {
    while (true) {
        let index = ~~(Math.random() * testimonials.length)
        if (testimonials.length != selected.length) {
            if (selected.indexOf(testimonials[index].id) === -1) {
                selected.push(testimonials[index].id)
                return testimonials[index]
            } 
        } else {
            return undefined
        }        
    }
}

var random1 = getTestimonials2(data)
var random2 = getTestimonials2(data)
var random3 = getTestimonials2(data)
var random4 = getTestimonials2(data)
var random5 = getTestimonials2(data)

console.log(random1)
console.log(random2)
console.log(random3)
console.log(random4)
console.log(random5)
James_F
  • 449
  • 2
  • 8
1

In order to keep users array intact, make a copy using users.slice(0);

Then, continue to generate a random number based on the usersCopy.length until usersCopy is empty. Call usersCopy.splice(random, 1) to get a random user while removing it from the usersCopy array.

Use document.getElementById and a count variable to get the correct element and edit its html. The only issue here is you have more html elements than elements in your array. It's up to you how to handle that situation. You could repeat the first elements in this situation and if there are more array elements than HTML elements, you can stop your while loop if count > number of HTML elements.

var users = [
    {
        name: 'Tom Levis',
        body: 'Toms text'
    },
    {
        name: 'Some Random Guy',
        body: 'Some guys text'
    },
    {
        name: 'Peter',
        body: 'Peters text'
    }
];

let usersCopy = users.slice(0);
let count = 0;

while (usersCopy.length) {
 count++;
 let random = ~~(Math.random() * usersCopy.length);
 let user = usersCopy.splice(random, 1)[0];
 document.getElementById('testimonial-' + count).innerHTML = user.name + '<br>' + '<p id="testimonial-text">' + user.body + '</p>';
}
<html>
<body>
<p id="testimonial-1"></p>
<p id="testimonial-2"></p>
<p id="testimonial-3"></p>
</body>
</html>
kilkfoe
  • 445
  • 5
  • 11
  • Thanks for the help buddy. It indeed works as i would like to have it in the console. Yet i am not sure yet how to use it using the HTML file itself. The problem is for each testimonial i need to make a new line that inserts it. It has to do with the way Owl Carousel defines multiple entries. If i make it via 1 line it will see it as only 1 testimonial or it generates multiple with the same value. I placed it in a pastebin to show the html entry also. https://pastebin.com/Rf6kGgtW – DevDokus Mar 27 '19 at 04:22
1

One solution is to create a shallow copy of the original array with Array.slice(), and later, every time you choice one new element, you delete that element from the copy using Array.splice() to ensure not using it again.

Example:

var user = [
  {name: 'Tom Levis', body: 'Tom Levis text'},
  {name: 'Jonah', body: 'Jonah text'},
  {name: 'Peter', body: 'Peters text'},
  {name: 'Albert', body: 'Albert text'},
  {name: 'Susan', body: 'Susan text'},
  {name: 'Dominic', body: 'Dominic text'}
];

let userCopy = user.slice();

for (let i = 1; i <= 5; i++)
{
    let rndIdx = ~~(Math.random() * userCopy.length);
    let testimonial = document.getElementById('testimonial-' + i);
    let user = userCopy[rndIdx];
    testimonial.innerHTML = `${user.name}<br><p id="testimonial-text">${user.body}</p>`;
    userCopy.splice(rndIdx, 1);
}
<p id="testimonial-1"></p>
<p id="testimonial-2"></p>
<p id="testimonial-3"></p>
<p id="testimonial-4"></p>
<p id="testimonial-5"></p>
Community
  • 1
  • 1
Shidersz
  • 16,846
  • 2
  • 23
  • 48