What is the best way in JavaScript given an array of ids:
var ids = ['ax6484', 'hx1789', 'qp0532'];
and a current id hx1789 to select another value at random that is not the current from the ids array?
Get the index of the value, generate a random value, if the random is the index, use 1 less (depending on random generated)
var random = Math.floor(Math.random() * ids.length)
var valueIndex = ids.indexOf("hx1789");
if (random === valueIndex) {
if (random === 0) {
random = 1;
} else {
random = random - 1;
}
}
var randomValue = ids[random];
Demo: http://jsfiddle.net/sxzayno7/
And yeah, test the array length, if it's 1 - probably don't want to do this! Thanks @ThomasStringer
if (ids.length > 1) { //do it! }
Or just filter out the value and pull against that:
var randomIndex = Math.floor(Math.random() * (ids.length - 1))
var random = ids.filter(function(id) {
return id !== "hx1789";
})[randomIndex]
filter route, but assigning to a new variable and then using the length of it instead of length - 1 of the original array. I think I like you're technique better, as I could make it work with no extra variables (not even for the index -- just put that within the []) on the last line.UnderscoreJS is your best friend!
_.sample(_.without(['ax6484', 'hx1789', 'qp0532'], 'hx1789'));
or with variables;
var myArray = ['ax6484', 'hx1789', 'qp0532'];
var currentId = 'hx1789';
var newRandomId = _.sample(_.without(myArray , currentId));
I would probably do something along the lines of:
var current = 'hx1789'; // whatever variable stores your current element
var random = Math.floor(Math.random() * ids.length);
if(random === ids.indexOf(current)) {
random = (random + 1) % ids.length;
}
current = ids[random];
Basically if the newly picked element sits on the same index, you just pick the next element from the array, or if that goes out of bounds, pick the first.
You could make a duplicate array, then remove the current indexed element from that array and select an random element from the duplicate with the removed item:
var ids = ['ax6484', 'hx1789', 'qp0532'];
var currentIndex = ids.indexOf('hx1789');
var subA = ids.slice(0);
subA.splice(currentIndex , 1);
var randItem = subA[Math.floor((Math.random() * subA.length))];
This gets trickier if your value is not a simple string such as hx1789 but rather for instance comes from an array within the array you want to generate a different value from:
let weekSegments = [["Su","Mo"],["Tu","We"],["Th","Fr","Sa"]];
If you have the index it's simple, no matter how deeply nested the array (or object) is and/or the types of the values it contains:
let index = 1;
let segment = weekSegments[index];
let randomIndex;
let randomSegment;
if (weekSegments.length >= 1) {
randomIndex = Math.floor(Math.random) * (weekSegments.length - 1);
randomSegment = weekSegments.filter((val, i) => i !== index)[randomIndex];
}
Without the index though things get more complicated due to having to test for array and/or object equality in the filter function. In the case above it's fairly easy because it's an array that is only one-level deep which in turn only contains simple values. First we create a string representation of the sorted segment values we already have, then compare that to a string representation of val on each filter iteration:
let segmentVals = segment.sort().join(',');
if (weekSegments.length > 1) {
randomIndex = Math.floor(Math.random) * (weekSegments.length - 1);
randomSegment = weekSegments.filter((val) => {
return segmentVal !== val.sort().join(',');
})[randomIndex];
}
When dealing with objects rather than arrays and/or arrays/objects that are deeply nested and/or contain non-nested values it devolves into the sticky matter of object equality that is probably not entirely germane to this Q&A