Swapping all elements of an array except for first and last - javascript

I have an array that looks like this
const x = ['A','B','C','D','E']
I want to have an elegant function that would shuffle the content of the array but keeps the first or the last element fixed. Something like customShuffle(x) which will shuffle the array but ensures that the element "A" will be in the first position and the element "E" will be at the last position. All other elements are shuffled.

If the first and last elements of the array always stay in that same place, you can apply a normal shuffling algorithm, like a modern variation of Fisher and Yates', skipping those positions:
function customShuffle(arr) {
if (arr.length < 3) {
return arr;
}
// Note the -2 (instead of -1) and the i > 1 (instead of i > 0):
for (let i = arr.length - 2; i > 1; --i) {
const j = 1 + Math.floor(Math.random() * i);
[arr[i], arr[j]] = [arr[j], arr[i]];
}
return arr;
}
console.log(customShuffle([1, 2, 3, 4, 5]).join(', '));
console.log(customShuffle(['A', 'B', 'C', 'D', 'E']).join(', '));
.as-console-wrapper {
max-height: 100vh;
}
Otherwise, if you want to choose the first and last elements, as you pointed out in your original question, you can do something like this:
Find the index of the elements you want to have in the first and last positions first: firstIndex and lastIndex.
If those elements exist (they might not be present), remove them from the array.
Apply a shuffling algorithm to the remaining elements (there's no need to also shuffle first and last).
Add the first and last elements back into their place, if you need to.
function customShuffle(arr, first, last) {
// Find and remove first and last:
const firstIndex = arr.indexOf(first);
if (firstIndex !== -1) arr.splice(firstIndex, 1);
const lastIndex = arr.indexOf(last);
if (lastIndex !== -1) arr.splice(lastIndex, 1);
// Normal shuffle with the remainign elements using ES6:
for (let i = arr.length - 1; i > 0; --i) {
const j = Math.floor(Math.random() * (i + 1));
[arr[i], arr[j]] = [arr[j], arr[i]];
}
// Add them back in their new position:
if (firstIndex !== -1) arr.unshift(first);
if (lastIndex !== -1) arr.push(last);
return arr;
}
console.log(customShuffle([1, 2, 3, 4, 5], 5, 1).join(', '));
console.log(customShuffle(['A', 'B', 'C', 'D', 'E'], 'E', 'C').join(', '));
console.log(customShuffle([1, 2, 3, 4, 5], 10, 20).join(', '));
.as-console-wrapper {
max-height: 100vh;
}

Using the shuffle algorithm from How to randomize (shuffle) a JavaScript array?
You can extend it like this:
function shuffle(array) {
var currentIndex = array.length, temporaryValue, randomIndex;
// While there remain elements to shuffle...
while (0 !== currentIndex) {
// Pick a remaining element...
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
// And swap it with the current element.
temporaryValue = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex] = temporaryValue;
}
return array;
}
function customShuffle(array, first, last) {
if (first) {
if (last) {
const updatedArray = shuffle(array).filter(item => item !== first && item !== last);
return [first, ...updatedArray, last];
}
const updatedArray = shuffle(array).filter(item => item !== first);
return [first, ...updatedArray];
}
return shuffle(array);
}

You could first generate new shuffled array and then check if first and last arguments are provided and take those elements and place them on first and last position.
const x = ['A', 'B', 'C', 'D', 'E']
function shuffle(arr, first, last) {
const newArr = arr.reduce((r, e, i) => {
const pos = parseInt(Math.random() * (i + 1))
r.splice(pos, 0, e)
return r;
}, []);
if (first) newArr.unshift(newArr.splice(newArr.indexOf(first), 1)[0]);
if (last) newArr.push(newArr.splice(newArr.indexOf(last), 1)[0])
return newArr
}
console.log(shuffle(x))
console.log(shuffle(x, "A", "E"))

You can do it like this. first and last params are optional.
Check if first is passed and if it is in the array. If so, then remove it from the array. Do the same for the last. Shuffle indices of the remaining array. Recreate new array based on the shuffled indices, as well as first and last arguments.
const shuffle = (arr, first, last) => {
let firstIn = false;
let lastIn = false;
if (first && arr.includes(first)) {
arr.splice(arr.indexOf(first), 1);
firstIn = true;
}
if (last && arr.includes(last)) {
arr.splice(arr.indexOf(last), 1);
lastIn = true;
}
const len = arr.length;
const used = [];
while (used.length !== len) {
let r = Math.floor(Math.random() * len);
if (!used.includes(r)) { used.push(r); }
}
const newArr = [];
if (first && firstIn) { newArr.push(first); }
for (let i = 0; i < len; i++) {
newArr.push(arr[used[i]]);
}
if (last && lastIn) { newArr.push(last); }
return newArr;
}
let arr = ['A', 'B', 'C', 'D', 'F'];
arr = shuffle(arr);
console.log(arr);
arr = shuffle(arr, 'A');
console.log(arr);
arr = shuffle(arr, 'A', 'B');
console.log(arr);
shuffle(arr); will shuffle the whole array.
arr = shuffle(arr, 'A'); will move A to the front and shuffle the rest.
arr = shuffle(arr, 'A', 'B'); will move A to the front, B to the end, and shuffle the rest.
Word of caution: while this approach is not in-place, it will still mutate the original array, because of the splice method.

Try something like this. It keeps the first and last elements in place without explicitly defining their values, and builds a new array with the other elements shuffled randomly.
const x = ['A','B','C','D','E'];
const shuffledArray = customShuffle(x);
console.log(shuffledArray);
function customShuffle(arr) {
let newArray = [];
const first = arr[0];
const last = arr[arr.length-1];
//First, remove the 'first' and 'last' values from array:
for(let i=0; i<arr.length; i++){
if(arr[i] == first || arr[i] == last){
arr.splice(i, 1);
}
}
//Next, add values to the new array at random:
for(let i=0; i<arr.length; i++){
const indexToRemove = Math.floor( Math.random() * arr.length );
const value = arr[indexToRemove];
arr.splice(indexToRemove, 1);
newArray.push(value);
}
//Last, add in the 'first' and 'last' values:
newArray.unshift(first);
newArray.push(last);
return newArray;
}

Please try the following simple solution.This will shuffle all the elements other than the first and the last element of the array (jsfiddle):
const x = ['A', 'B', 'C', 'D', 'E'];
CustomShuffle(x);
function CustomShuffle(x) {
//shuffle the elements in between first and the last
var max = x.length - 2;
var min = 1;
for (var i = max; i >= min; i--) {
var randomIndex = Math.floor(Math.random() * (max - min + 1)) + min;
var itemAtIndex = x[randomIndex];
x[randomIndex] = x[i];
x[i] = itemAtIndex;
}
alert(x);
}
In case first and last elements are not in place beforehand, you may try the following (jsfiddle):
const x = ['A', 'B', 'C', 'D', 'E'];
CustomShuffle(x, first = "B", last = "A");
function CustomShuffle(x, first, last) {
//position first element correctly
var indexToSwap = x.indexOf(first);
if (indexToSwap != 0) {
x = SwapValuesAtIndices(x, indexToSwap, 0);
}
//position last element correctly
indexToSwap = x.indexOf(last);
if (indexToSwap != x.length - 1) {
x = SwapValuesAtIndices(x, indexToSwap, x.length - 1);
}
//randomly shuffle the remaining elements in between
var max = x.length - 2;
var min = 1;
for (var i = max; i >= min; i--) {
var randomIndex = Math.floor(Math.random() * (max - min + 1)) + min;
var itemAtIndex = x[randomIndex];
x[randomIndex] = x[i];
x[i] = itemAtIndex;
}
alert(x);
}
function SwapValuesAtIndices(array, firstIndex, secondIndex) {
var temp = array[firstIndex];
array[firstIndex] = array[secondIndex];
array[secondIndex] = temp;
return array;
}
Further reading:
Shuffling an array
Generating a random number in a given
range
Swapping elements

You can use this function which uses the modern version of the Fisher–Yates shuffle algorithm to shuffle the sub-array x.slice(1, x.length - 1), which is x with the exclusion of the first and last elements, then adds them back to the shuffled sub-array:
const x = ['A','B','C','D','E'];
function customShuffle(x) {
var y = x.slice(1, x.length - 1);
var j, t, i;
for (i = y.length - 1; i > 0; i--) {
j = Math.floor(Math.random() * (i + 1));
t = y[i];
y[i] = y[j];
y[j] = t;
}
return [x[0]].concat(y).concat(x[x.length-1]);
}
console.log(customShuffle(x));
console.log(customShuffle(x));
console.log(customShuffle(x));
console.log(customShuffle(x));

Because you asked for elegant, I like to implement a more functional style of programming here. The code below does what you want. You supple the shuffle function with your array, the max number of times you want it shuffled (the higher the number, the better the shuffle is), and true to keep the first element in place, false to keep the last.
function shuffle(array, maxTimes, first) {
var temp = (first) ? array.reverse().pop() : array.pop();
Array.from(
Array(Math.round(Math.random()*maxTimes))
.keys()).forEach(val => array = array.reduce((acc,val) =>
(Math.random() > 0.5) ? acc.concat([val]) : [val].concat(acc),[]));
return (first) ? [temp].concat(array.reverse()) : array.concat([temp]);
}
Example usage:
shuffle(['A','B','C','D','E'], 10, true);
Output: ["A", "C", "D", "B", "E"]
I hope this is what you're looking for and answers your question.
Edit
Turns out you can get the shuffle logic all in one line (when removing the unnecessary newlines). When you add the two lines to retain the first or last character, you can essentially create this function with three lines of code.

function SpecialShuffle(MyArray)
{
var newArray = [];
var counter= 1;
for(var i = MyArray.length-1 ; i>-1 ; i--)
{
if(i == MyArray.length)
{
newArray[i] = MyArray[i]
}
else if(i == 0 )
{
newArray[i]=MyArray[i];
}
else
{
newArray[counter] = MyArray[i];
counter++;
}
}
return newArray;
}

Related

Find the second largest number in array

I have an array of three element like [31,23,12] and I want to find the second largest element and its related position without rearranging the array.
Example :
array = [21,23,34]
Second_largest = 23;
Position is = 1;
Make a clone of your original array using .slice(0) like :
var temp_arr = arr.slice(0);
Then sor it so you get the second largest value at the index temp_arr.length - 2 of your array :
temp_arr.sort()[temp_arr.length - 2]
Now you could use indexOf() function to get the index of this value retrieved like :
arr.indexOf(second_largest_value);
var arr = [23, 21, 34, 34];
var temp_arr = [...new Set(arr)].slice(0); //clone array
var second_largest_value = temp_arr.sort()[temp_arr.length - 2];
var index_of_largest_value = arr.indexOf(second_largest_value);
console.log(second_largest_value);
console.log(index_of_largest_value);
Using ES6 Set and Array.from
const secondLargest = (arr) => Array.from([...new Set(arr)]).sort((a,b) => b-a)[1]
Above function removes duplicate elements using Set and returns the second largest element from the sorted array.
I tried to make the answer as simple as possible here, you can it super simple
function getSecondLargest(nums) {
var flarge = 0;
var slarge = 0;
for (var i = 0; i < nums.length; i++) {
if (flarge < nums[i]) {
slarge = flarge;
flarge = nums[i];
} else if (nums[i] > slarge) {
slarge = nums[i]
}
}
return slarge;
}
Its fully logical ,there is no array sort or reverse here, you can also use this when values are duplicate in aray.
function getSecondLargest(nums) {
nums.sort(function(x,y){
return y-x;
});
for(var j=1; j < nums.length; j++)
{
if(nums[j-1] !== nums[j])
{
return nums[j];
}
}
}
getSecondLargest([1,2,3,4,5,5]);
OUTPUT: 4
This method will also take care of the multiple occurrence of a number in the array. Here, we are first sorting the array and then ignoring the same number and returning our answer.
You could create a copy of the original array using spread and sort() it. From you'd just get the second to last number from the array and use indexOf to reveal it's index.
const array = [21,23,34];
const arrayCopy = [...array];
const secondLargestNum = arrayCopy.sort()[arrayCopy.length - 2]
console.log(array.indexOf(secondLargestNum));
Alternatively you can use concat to copy the array if compatibility is an issue:
var array = [21, 23, 34];
var arrayCopy = [].concat(array);
var secondLargestNum = arrayCopy.sort()[arrayCopy.length - 2]
console.log(array.indexOf(secondLargestNum));
This way is the most verbose, but also the most algorithmically efficient. It only requires 1 pass through the original array, does not require copying the array, nor sorting. It is also ES5 compliant, since you were asking about supportability.
var array = [21,23,34];
var res = array.reduce(function (results, curr, index) {
if (index === 0) {
results.largest = curr;
results.secondLargest = curr;
results.indexOfSecondLargest = 0;
results.indexOfLargest = 0;
}
else if (curr > results.secondLargest && curr <= results.largest) {
results.secondLargest = curr;
results.indexOfSecondLargest = index;
}
else if (curr > results.largest) {
results.secondLargest = results.largest;
results.largest = curr;
results.indexOfSecondLargest = results.indexOfLargest;
results.indexOfLargest = index;
}
return results;
}, {largest: -Infinity, secondLargest: -Infinity, indexOfLargest: -1, indexOfSecondLargest: -1});
console.log("Second Largest: ", res.secondLargest);
console.log("Index of Second Largest: ", res.indexOfSecondLargest);
I recently came across this problem, but wasn't allowed to use looping. I managed to get it working using recursion and since no one else suggested that possibility, I decided to post it here. :-)
let input = [29, 75, 12, 89, 103, 65, 100, 78, 115, 102, 55, 214]
const secondLargest = (arr, first = -Infinity, second = -Infinity, firstPos = -1, secondPos = -1, idx = 0) => {
arr = first === -Infinity ? [...arr] : arr;
const el = arr.shift();
if (!el) return { second, secondPos }
if (el > first) {
second = first;
secondPos = firstPos;
first = el;
firstPos = idx;
} if (el < first && el > second) {
second = el;
secondPos = idx;
}
return secondLargest(arr, first, second, firstPos, secondPos, ++idx);
}
console.log(secondLargest(input));
// {
// second: 115,
// secondPos: 8
// }
Hope this helps someone in my shoes some day.
Simple recursive function to find the n-largest number without permutating any array:
EDIT: Also works in case of multiple equal large numbers.
let array = [11,23,34];
let secondlargest = Max(array, 2);
let index = array.indexOf(secondlargest);
console.log("Number:", secondlargest ,"at position", index);
function Max(arr, nth = 1, max = Infinity) {
let large = -Infinity;
for(e of arr) {
if(e > large && e < max ) {
large = e;
} else if (max == large) {
nth++;
}
}
if(nth==0) return max;
return Max(arr, nth-1, large);
}
Just to get 2nd largest number-
arr = [21,23,34];
secondLargest = arr.slice(0).sort(function(a,b){return b-a})[1];
To get 2nd largest number with index in traditional manner-
arr = [20,120,111,215,54,78];
max = -Infinity;
max2 = -Infinity;
indexMax = -Infinity;
index2 = -Infinity;
for(let i=0; i<arr.length; i++) {
if(max < arr[i]) {
index2 = indexMax;
indexMax = i;
max2 = max;
max = arr[i];
} else if(max2 < arr[i]) {
index2 = i;
max2 = arr[i];
}
}
console.log(`index: ${index2} and max2: ${max2}`);
I have tried to solve without using the inbuilt function.
var arr = [1,2, -3, 15, 77, 12, 55];
var highest = 0, secondHighest = 0;
// OR var highest = arr[0], secondHighest = arr[0];
for(var i=0; i<arr.length; i++){
if(arr[i] > highest){
secondHighest = highest;
highest = arr[i];
}
if(arr[i] < highest && arr[i] > secondHighest){
secondHighest = arr[i];
}
}
console.log('>> highest number : ',highest); // 77
console.log('>> secondHighest number : ',secondHighest); // 55
var arr = [21,23,34];
var output = getSecondLargest(arr);
document.getElementById("output").innerHTML = output;
function getSecondLargest(nums) {
if (nums.length == 0){
return undefined;
}
nums.sort((a,b) => b-a);
var newArr = [...new Set(nums)];
return newArr[1];
}
<p id="output"></p>
function getSecondLargest(nums) {
const sortedArray = new Set(nums.sort((a, b) => b - a)).values();
sortedArray.next();
return sortedArray.next().value;
}
console.log(getSecondLargest([1, 2, 4, 4, 3]));
//Suggest making unique array before checking largest value in the array
function getSecondLargest(arr) {
let uniqueChars = [...new Set(arr)];
let val=Math.max(...uniqueChars);
let arr1 = arr.filter(function(item) {
return item !== val;
})
let num=Math.max(...arr1);
return num;
}
function main() {
const n = +(readLine());
const nums = readLine().split(' ').map(Number);
console.log(getSecondLargest(nums));
}
Here the code will give the second largest number and the index of it
const a = [1, 2, 3, 4, 6, 7, 7, 8, 15]
a.sort((a,b)=>a-b) //sorted small to large
const max = Math.max(...a)
const index = a.indexOf(max)
const s = {secondLargest:a[index-1],index:index-1}
console.log(s)
var elements = [21,23,34]
var largest = -Infinity
// Find largest
for (var i=0; i < elements.length; i++) {
if (elements[i] > largest) largest = elements[i]
}
var second_largest = -Infinity
var second_largest_position = -1
// Find second largest
for (var i=0; i < elements.length; i++) {
if (elements[i] > second_largest && elements[i] < largest) {
second_largest = elements[i]
second_largest_position = i
}
}
console.log(second_largest, second_largest_position)
function getSecondLargest(nums) {
let arr = nums.slice();//create a copy of the input array
let max = Math.max(...arr);//find the maximum element
let occ = 0;
for(var i = 0 ; i < arr.length ; i++)
{
if(arr[i] == max)
{
occ = occ +1;//count the occurrences of maximum element
}
}
let sortedArr =arr.sort(function(x, y) { return x > y; } );//sort the array
for(var i = 1 ; i <= occ ; i++){
sortedArr.pop()//remove the maximum elements from the sorted array
}
return Math.max(...sortedArr);//now find the largest to get the second largest
}
I write the most simple function with O(n) complexity using two variables max and secondMax with simple swapping logic.
function getSecondLargest(nums) {
let max = 0, secondMax = 0;
nums.forEach((num) => {
if (num > max) {
secondMax = max;
max = num;
} else if (num != max && num > secondMax) secondMax = num;
});
return secondMax;
}
here you can also deal with if the second largest or largest number is repeated
var nums =[2,3,6,6,5];
function getSecondLargest(nums) {
let secondlargets;
nums.sort(function(a, b){return a - b});
// all elements are in the accesindg order
// [1,2,3,5,6,6]
var highest;
// that is the last sorted element
highest = nums[nums.length-1];
nums.pop();
// through above statment we are removing the highest element
for(let i =0;i<nums.length-1;i++){
if(nums[nums.length-1]==highest){
/* here we remove gives this as conditon because might be the hiesht
had more indecis as we have in this question index(5) &index(6)
so remove the element till all positon have elemnt excepts the highest */
nums.pop()
}
else{
return nums[nums.length-1]
/* our array is already sorted and after removing thew highest element */
}
}
}
Please find a simple solution, without using inbuild functions:
Time complexity is O(n)
function secondLargest(arr) {
let prev = [0]
let i =1;
let largest =0;
while(i<arr.length){
let current = arr[i];
if(current > largest ) {
largest = current;
prev = arr[i-1];
} else if (current > prev && current < largest) {
prev = current
}
i++;
}
return prev;
}
let arr = [1,2,3,41,61,10,3,5,23];
console.log(secondLargest(arr));
Here is a simple solution using .sort() and new Set()
const array = [21, 23, 34, 34];
function getSecondLargest(arr){
return list = [...new Set(arr)].sort((a, b) => b - a)[1]
};
getSecondLargest(array);
In this case, if there are repeated numbers then they will be removed, and then will sort the array and find the second-largest number.
let arr=[12,13,42,34,34,21,42,39]
let uniqueArray=[...new Set(arr)]
let sortedArray=uniqueArray.sort((a,b)=>b-a)
console.log(sortedArray[1])
/* we can solve it with recursion*/
let count = 0; /* when find max then count ++ */
findSecondMax = (arr)=> {
let max = 0; /* when recursive function call again max will reinitialize and we get latest max */
arr.map((d,i)=>{
if(d > max) {
max = d;
}
if(i == arr.length -1) count++;
})
/* when count == 1 then we got out max so remove max from array and call recursively again with rest array so now count will give 2 here we go with 2nd max from array */
return count == 1 ? findSecondMax(arr.slice(0,arr.indexOf(max)).concat(arr.slice(arr.indexOf(max)+1))) : max;
}
console.log(findSecondMax([1,5,2,3]))
function getSecondLargest(nums) {
// Complete the function
var a = nums.sort();
var max = Math.max(...nums);
var rev = a.reverse();
for(var i = 0; i < nums.length; i++) {
if (rev[i] < max) {
return rev[i];
}
}
}
var nums = [2,3,6,6,5];
console.log( getSecondLargest(nums) );
Find Second Highest Number (Array contains duplicate values)
const getSecondHighestNumber = (numbersArry) => {
let maxNumber = Math.max( ...numbersArry);
let arrFiltered = numbersArry.filter(val => val != maxNumber);
return arrFiltered.length ? Math.max(...arrFiltered) : -1;
}
let items = ["6","2","4","5","5","5"];
const secondHighestVal = getSecondHighestNumber(items);
console.log(secondHighestVal); // 5
const arrData = [21, 23, 34];
const minArrValue = Math.min(...arrData);
const maxArrValue = Math.max(...arrData);
let targetHighValue = minArrValue;
let targetLowValue = maxArrValue;
for (i = 0; i < arrData.length; i++) {
if (arrData[i] < maxArrValue && arrData[i] > targetHighValue) {
targetHighValue = arrData[i];
}
if (arrData[i] > minArrValue && arrData[i] < targetLowValue) {
targetLowValue = arrData[i];
}
}
console.log('Array: [' + arrData + ']');
console.log('Low Value: ' + minArrValue);
console.log('2nd Lowest Value: ' + targetLowValue);
console.log('2nd Highest Value: ' + targetHighValue);
console.log('High Value: ' + maxArrValue);
Notice that if the max number appears multiple times in your array (like [6, 3,5,6,3,2,6]), you won't get the right output. So here is my solution:
function getSecondLargest(nums) {
// Complete the function
const sortedNumbers = nums.sort((a, b) => b - a);
const max = sortedNumbers[0];
const secondMax = sortedNumbers.find(number => number < max);
return secondMax;
}
const findSecondlargestNumber = (data) => {
let largest = null;
let secondlargest = null;
data.forEach(num => {
if(!largest) {
largest = num;
}
else if(num > largest) {
secondlargest = largest;
largest = num;
}
else if((!secondlargest && num !== largest) || (secondlargest)) {
secondlargest = num;
}
})
return secondlargest;
}
console.log(findSecondlargestNumber([11, 11, 3, 5, 6,2, 7]))
Here's the simplest way to get the second largest number and it's respective position from an array without rearranging the array or without using sorting method.
function findSecondLargest(arr) {
const largest = Math.max.apply(null, arr);
arr[arr.indexOf(largest)] = -Infinity;
const secondLargest = Math.max.apply(null, arr);
const position = arr.indexOf(secondLargest);
return { secondLargest, position };
}
console.log(findSecondLargest([3, 5, 7, 9, 11, 13])); //{ secondLargest: 11, position: 4 }
-Infinity is smaller than any negative finite number.
function getSecondLargest(nums) {
const len = nums.length;
const sort_arr = nums.sort();
var mynum = nums[len-1];
for(let i=len; i>0; i--){
if(mynum>nums[i-1]){
return nums[i-1];
}
}
}

Reversing an array without 'reverse' or duplicating an array

I'm trying to solve the following exercise:
Reverse an array without using the reverse method, without using a
second array, and without duplicating any of the values.
I've thought about making the array an object and then updating the array from the end to the beginning but I figured you can just update it as well.
Tried something simple like:
function reverseArray(array) {
for (var i = 0; i < array.length; i++) {
// var elem = array.shift();
var elem = array.shift()
array.push(elem)
}
return array
}
array = ['a', 'b','c','d','e'];
reverseArray(array);
But that doesn't really change it. Any advice or explanation on how to do this?
With ES6 syntax you don't need to copy a value into a temporary variable (is that what the last requirement is about?).
function reverse(arr) {
for(let i = 0, j = arr.length-1; i < j; i++, j--)
[arr[i], arr[j]] = [arr[j], arr[i]];
}
const arr = ['a','b','c','d','e'];
reverse(arr);
console.log(arr);
One may argue that arrays are created here (if engines don't optimise this away), just like splice also creates an array (as its return value).
array = ['a', 'b', 'c', 'd', 'e'];
console.log(array);
for (var i = 0; i < Math.floor(array.length / 2); i++) {
var item = array[i];
array[i] = array[array.length - i - 1];
array[array.length - i - 1] = item;
}
console.log(array);
Here is a minimal approach. Given var arr = [1,2,3,4], this loop will mutate arr to [4,3,2,1]:
for (var i = 0; i < arr.length - 1; i++) {
arr.splice(i, 0, arr.pop());
}
The following will work reverse an array without using the reverse method. It works by swapping the first and last elements, then the second and second-to-last elements, then the third and third-to-last elements, etc until the i is no longer less than (<) than j.
function reverse(arr) {
for(var i = 0, j = arr.length-1; i < j; i++, j--) {
var tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
return arr;
};
var reversed = reverse(['a','b','c','d','e']);
console.log(reversed);
https://jsfiddle.net/pa2fqa8n/1/
a = ['a', 'b', 'c', 'd', 'e'];
for(var i = 0; i < a.length-1; i++){
for(var j = 0; j < a.length-i-1; j++){
var k = a[j];
a[j] = a[j+1];
a[j+1] = k;
}
}
First iteration of inner loop moves the first element to the end, and the rest of the elements forward once. Each following iteration does the same thing, but 1 less than the previous iteration.
I had to use a swap variable, does that violate "without duplicating any of the values"?
var test1 = [2, '5', 6, 'a', 'Z'];
var test2 = [2, '5', false, 'a', 'Z', {a: 'whatevs'}];
console.log('test1 before', JSON.stringify(test1));
console.log('test2 before', JSON.stringify(test2));
reversarooni(test1);
reversarooni(test2);
console.log('test1 after', JSON.stringify(test1));
console.log('test2 after', JSON.stringify(test2));
function reversarooni(inputArray) {
var index = 0;
var len = inputArray.length;
for(; index < len / 2; index++) {
var swap = inputArray[index];
inputArray[index] = inputArray[(len - 1) - index];
inputArray[(len - 1) - index] = swap;
}
}
Here's how, without copies, temporary arrays or variables to hold values, or using Array.reverse().Modifying the array in place
function reverseArray(array) {
var len = array.length;
for (var i=len,j=-1; j++,i--;) array.unshift( array[len-1-i+(j)] );
array.length = len;
}
var array = ['a', 'b','c','d','e'];
reverseArray(array);
console.log(array);
It inserts the values backwards into the beginning of the array, pushing the old values to the end, and then slicing them of by resetting the arrays length after the iteration has completed.
You could use a spread syntax ..., rest parameters ... and return the swapped items with a recursive and functional approach.
const
_ = (...a) => a,
rev = (a, ...rest) => rest.length ? _(...rev(...rest), a) : _(a),
reverseArray = array => rev(...array);
console.log(reverseArray(['a', 'b', 'c', 'd', 'e']));
console.log(reverseArray(['a']));
console.log(reverseArray(['a', 'b']));
.as-console-wrapper { max-height: 100% !important; top: 0; }
function reverseArray(a) {
const halfLength = a.length / 2;
for (let i = 0; i< halfLength; i++){
const start = a[i]
a[i] = a[a.length-i-1]
a[a.length-i-1] = start
}
return a;
}
function printReverse(array) {
for (i = array.length-1; i > -1; i--) {
console.log(array[i]); //4,3,2,1
}
}
printReverse([1, 2, 3, 4]);
This worked for me.

How to slice array values by two

I have a javascript array of values [A,B,C,D,E.....] that i need to split with this syntax:
[ [A, B], [C, D], [E, F] ]
and so on (it will be always a pair).
So it should be a loop that returns a string.
I've tried to do like so, and it's almost what i wanted:
text = '['+array[0]+','+array[1]+']';
for (index = 2; index < array.length; index++) {
text += '['+array[index]+','+array[index+1]+']';
console.log(text);
}
I get
[10:00,15:45][18:30,20:00]
[10:00,15:45][18:30,20:00][20:00,undefined]
instead i need to get only this:
[10:00,15:45][18:30,20:00]
Could try making text an actual array and pushing pairs in, like so
var orig=['A','B','C','D','E','F'];
var text=[];
for (index = 0; index < orig.length-1; index+=2) {
text.push([orig[index],orig[index+1]]);
console.log(JSON.stringify(text));
}
Here's a small method for grouping array elements. You can just check that your inner arrays have a length of 2 afterwards, if you only care about pairs.
if (!Array.prototype.group) {
Array.prototype.group = function (length) {
var a = this.slice(), r = [];
length = length || 1;
while (a.length > 0) {
r.push(a.splice(0, length));
}
return r;
};
}
var arr = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'].group(2);
console.log(arr);
A version for if you want to discard a final group that doesn't match your expected length.
if (!Array.prototype.group) {
Array.prototype.group = function (length, req) {
var a = this.slice(), r = [], h;
length = length || 1;
while (a.length > 0) {
h = a.splice(0, length);
if (!req || h.length === length) {
r.push(h);
}
}
return r;
};
}
var arr = ['an', 'odd', 'number', 'of', 'elements'].group(2, true);
console.log(arr)
Something in the lines of:
var groupArray = function(arr){
var groupedArray = [];
for(var i = 0, len = arr.length; i < len; i += 2){
groupedArray.push([arr[i], arr[i + 1]]);
}
return groupedArray;
};
This obviously only works for arrays with an even number of elements. If you need the function to work with arrays with an odd number of elements, too, you need to acomodate for that.

Decrease the probability of getting random item from array same as previous one

Say, I've got an array like
var arr = [1,2,3,4,5,6,7,8,9,10,11,12];
and I wanna get random array item, but later I would like to re-randomize my current item. What is the efficient way to exclude or reduce the chance of getting the same item once again?
Does stuff like this really help:
current != arr[Math.floor(Math.random() * 12)] ? current = arr[Math.floor(Math.random() * 12)] : arr[Math.floor(Math.random() * 12)];
I mean, would it recalculate random array index each time or just link to the same value?
What is a better way?
If you can keep array unsorted: (if not, you can use array which only contains indices of elements in first array)
var array = [ ... ];
var len = array.length;
function getRandomItem(){
if (len <= 0) len = array.length;
var item = Math.floor(Math.random()*len--);
var x = array[item];
array[item] = array[len];
array[len] = x;
return array[len];
}
Idea behind is to exclude already dispatched items by placing them outside of item fetching range. Function getRandomItem() will not return same item twice until all other elements also will be returned.
Following modification will only prevent function to return same element which was returned during previous call, as requested.
var array = [ 3, 1, 4, 5, 9, 2, 6, 8, 7 ];
var len = array.length-1;
function getRandomItem(){
if (len == 0) return array[0];
var item = Math.floor(Math.random()*len);
var x = array[item];
array[item] = array[len];
array[len] = x;
return array[len];
}
document.write("Starting array: " + array + "<br/>");
document.write("Selected value: " + getRandomItem() + "<br/>");
document.write("Resulting array: " + array);
Also see Fisher–Yates shuffle
I think the best solution is to put a while loop to check if the value is similar to the previous one or not.
var arr = [1,2,3,4,5,6,7,8,9,10,11,12];
Then you write:
var last_generated_value = null;
var val = Math.random();
val = Math.floor(val * 12);
while(last_generated_val == val)
{
val = Math.random();
val = Math.floor(val * 12);
}
last_generated_value = val;
Though you may put the above code block in a parent loop or a function to generate a concatenated set of values(in your case, number).
There are so many ways to achieve this. Maybe add a weight to each value and consider it in your random number selection? Then when you get it, reduce its weight. For example:
var weights = {};
var max = 12;
function initializeWeights() {
var i;
for (i = 1; i <= max; ++i) {
weights[i] = 100;
}
}
function getPseudoRandom() {
var possible_values = [], i, j;
for (i = 1; i <= max; ++i) {
for (j = 0; j < weights[i]; ++j) {
possible_values.push(i);
}
}
random_index = Math.floor(Math.random() * possible_values.length) + 1;
random = possible_values[random_index];
weights[random] = weights[random] - 10;
return random;
}
initializeWeights()
alert(getPseudoRandom());
Then you just have to figure out what to do when you reach 0. Maybe you can increment all the weights by 100.
Try
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
var clone = arr.slice();
var res = [];
do {
res.push(clone.splice(Math.random() * clone.length, 1)[0])
} while (clone.length !== 0);
// do stuff with res
document.write(JSON.stringify(res));
document.write(res[Math.floor(Math.random() * res.length)]);
console.log(arr);
perhaps this is ok? It seems to work, but maybe I'm missing some details?
var current = arr[Math.floor(Math.random() * 12)];
var prev = current;
do { current = arr[Math.floor(Math.random() * 12)]; }
while (current == prev);
You could randomly shuffle the array using a version of the Fisher-Yates algorithm and then just iterate through it:
var arr = [1,2,3,4,5,6,7,8,9,10,11,12],
shuffle = function(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
},
randomisedArray = shuffle(arr),
nextItem = function(){
return randomisedArray.pop();
};
while(randomisedArray.length>0){
console.log(nextItem());
}
You can do this with a single calculation:
// Just like before...
var arr = [1,2,3,4,5,6,7,8,9,10,11,12];
// Initialize first random index
var currentIndex = Math.floor(Math.random() * arr.length);
// And now...
function Next() {
currentIndex = (Math.floor(Math.random() * (arr.length - 1)) +
1 + currentIndex) % arr.length;
return currentIndex;
}
// Or if you want the next value...
function NextValue() {
return arr[Next()];
}
The idea is that you always randomize how many items to advance, with a maximum of (length - 1), and use modulo to truncate the index into the valid range.

JavaScript - Generating combinations from n arrays with m elements [duplicate]

This question already has answers here:
Cartesian product of multiple arrays in JavaScript
(35 answers)
Closed 1 year ago.
I'm having trouble coming up with code to generate combinations from n number of arrays with m number of elements in them, in JavaScript. I've seen similar questions about this for other languages, but the answers incorporate syntactic or library magic that I'm unsure how to translate.
Consider this data:
[[0,1], [0,1,2,3], [0,1,2]]
3 arrays, with a different number of elements in them. What I want to do is get all combinations by combining an item from each array.
For example:
0,0,0 // item 0 from array 0, item 0 from array 1, item 0 from array 2
0,0,1
0,0,2
0,1,0
0,1,1
0,1,2
0,2,0
0,2,1
0,2,2
And so on.
If the number of arrays were fixed, it would be easy to make a hard coded implementation. But the number of arrays may vary:
[[0,1], [0,1]]
[[0,1,3,4], [0,1], [0], [0,1]]
Any help would be much appreciated.
Here is a quite simple and short one using a recursive helper function:
function cartesian(...args) {
var r = [], max = args.length-1;
function helper(arr, i) {
for (var j=0, l=args[i].length; j<l; j++) {
var a = arr.slice(0); // clone arr
a.push(args[i][j]);
if (i==max)
r.push(a);
else
helper(a, i+1);
}
}
helper([], 0);
return r;
}
Usage:
cartesian([0,1], [0,1,2,3], [0,1,2]);
To make the function take an array of arrays, just change the signature to function cartesian(args) instead of using rest parameter syntax.
I suggest a simple recursive generator function:
// JS
function* cartesianIterator(head, ...tail) {
const remainder = tail.length ? cartesianIterator(...tail) : [[]];
for (let r of remainder) for (let h of head) yield [h, ...r];
}
// get values:
const cartesian = items => [...cartesianIterator(items)];
console.log(cartesian(input));
// TS
function* cartesianIterator<T>(items: T[][]): Generator<T[]> {
const remainder = items.length > 1 ? cartesianIterator(items.slice(1)) : [[]];
for (let r of remainder) for (let h of items.at(0)!) yield [h, ...r];
}
// get values:
const cartesian = <T>(items: T[][]) => [...cartesianIterator(items)];
console.log(cartesian(input));
You could take an iterative approach by building sub arrays.
var parts = [[0, 1], [0, 1, 2, 3], [0, 1, 2]],
result = parts.reduce((a, b) => a.reduce((r, v) => r.concat(b.map(w => [].concat(v, w))), []));
console.log(result.map(a => a.join(', ')));
.as-console-wrapper { max-height: 100% !important; top: 0; }
After doing a little research I discovered a previous related question:
Finding All Combinations of JavaScript array values
I've adapted some of the code from there so that it returns an array of arrays containing all of the permutations:
function(arraysToCombine) {
var divisors = [];
for (var i = arraysToCombine.length - 1; i >= 0; i--) {
divisors[i] = divisors[i + 1] ? divisors[i + 1] * arraysToCombine[i + 1].length : 1;
}
function getPermutation(n, arraysToCombine) {
var result = [],
curArray;
for (var i = 0; i < arraysToCombine.length; i++) {
curArray = arraysToCombine[i];
result.push(curArray[Math.floor(n / divisors[i]) % curArray.length]);
}
return result;
}
var numPerms = arraysToCombine[0].length;
for(var i = 1; i < arraysToCombine.length; i++) {
numPerms *= arraysToCombine[i].length;
}
var combinations = [];
for(var i = 0; i < numPerms; i++) {
combinations.push(getPermutation(i, arraysToCombine));
}
return combinations;
}
I've put a working copy at http://jsfiddle.net/7EakX/ that takes the array you gave earlier ([[0,1], [0,1,2,3], [0,1,2]]) and outputs the result to the browser console.
const charSet = [["A", "B"],["C", "D", "E"],["F", "G", "H", "I"]];
console.log(charSet.reduce((a,b)=>a.flatMap(x=>b.map(y=>x+y)),['']))
Just for fun, here's a more functional variant of the solution in my first answer:
function cartesian() {
var r = [], args = Array.from(arguments);
args.reduceRight(function(cont, factor, i) {
return function(arr) {
for (var j=0, l=factor.length; j<l; j++) {
var a = arr.slice(); // clone arr
a[i] = factor[j];
cont(a);
}
};
}, Array.prototype.push.bind(r))(new Array(args.length));
return r;
}
Alternative, for full speed we can dynamically compile our own loops:
function cartesian() {
return (cartesian.cache[arguments.length] || cartesian.compile(arguments.length)).apply(null, arguments);
}
cartesian.cache = [];
cartesian.compile = function compile(n) {
var args = [],
indent = "",
up = "",
down = "";
for (var i=0; i<n; i++) {
var arr = "$"+String.fromCharCode(97+i),
ind = String.fromCharCode(105+i);
args.push(arr);
up += indent+"for (var "+ind+"=0, l"+arr+"="+arr+".length; "+ind+"<l"+arr+"; "+ind+"++) {\n";
down = indent+"}\n"+down;
indent += " ";
up += indent+"arr["+i+"] = "+arr+"["+ind+"];\n";
}
var body = "var res=[],\n arr=[];\n"+up+indent+"res.push(arr.slice());\n"+down+"return res;";
return cartesian.cache[n] = new Function(args, body);
}
var f = function(arr){
if(typeof arr !== 'object'){
return false;
}
arr = arr.filter(function(elem){ return (elem !== null); }); // remove empty elements - make sure length is correct
var len = arr.length;
var nextPerm = function(){ // increase the counter(s)
var i = 0;
while(i < len)
{
arr[i].counter++;
if(arr[i].counter >= arr[i].length){
arr[i].counter = 0;
i++;
}else{
return false;
}
}
return true;
};
var getPerm = function(){ // get the current permutation
var perm_arr = [];
for(var i = 0; i < len; i++)
{
perm_arr.push(arr[i][arr[i].counter]);
}
return perm_arr;
};
var new_arr = [];
for(var i = 0; i < len; i++) // set up a counter property inside the arrays
{
arr[i].counter = 0;
}
while(true)
{
new_arr.push(getPerm()); // add current permutation to the new array
if(nextPerm() === true){ // get next permutation, if returns true, we got them all
break;
}
}
return new_arr;
};
Here's another way of doing it. I treat the indices of all of the arrays like a number whose digits are all different bases (like time and dates), using the length of the array as the radix.
So, using your first set of data, the first digit is base 2, the second is base 4, and the third is base 3. The counter starts 000, then goes 001, 002, then 010. The digits correspond to indices in the arrays, and since order is preserved, this is no problem.
I have a fiddle with it working here: http://jsfiddle.net/Rykus0/DS9Ea/1/
and here is the code:
// Arbitrary base x number class
var BaseX = function(initRadix){
this.radix = initRadix ? initRadix : 1;
this.value = 0;
this.increment = function(){
return( (this.value = (this.value + 1) % this.radix) === 0);
}
}
function combinations(input){
var output = [], // Array containing the resulting combinations
counters = [], // Array of counters corresponding to our input arrays
remainder = false, // Did adding one cause the previous digit to rollover?
temp; // Holds one combination to be pushed into the output array
// Initialize the counters
for( var i = input.length-1; i >= 0; i-- ){
counters.unshift(new BaseX(input[i].length));
}
// Get all possible combinations
// Loop through until the first counter rolls over
while( !remainder ){
temp = []; // Reset the temporary value collection array
remainder = true; // Always increment the last array counter
// Process each of the arrays
for( i = input.length-1; i >= 0; i-- ){
temp.unshift(input[i][counters[i].value]); // Add this array's value to the result
// If the counter to the right rolled over, increment this one.
if( remainder ){
remainder = counters[i].increment();
}
}
output.push(temp); // Collect the results.
}
return output;
}
// Input is an array of arrays
console.log(combinations([[0,1], [0,1,2,3], [0,1,2]]));
You can use a recursive function to get all combinations
const charSet = [["A", "B"],["C", "D", "E"],["F", "G", "H", "I"]];
let loopOver = (arr, str = '', final = []) => {
if (arr.length > 1) {
arr[0].forEach(v => loopOver(arr.slice(1), str + v, final))
} else {
arr[0].forEach(v => final.push(str + v))
}
return final
}
console.log(loopOver(charSet))
This code can still be shorten using ternary but i prefer the first version for readability 😊
const charSet = [["A", "B"],["C", "D", "E"],["F", "G", "H", "I"]];
let loopOver = (arr, str = '') => arr[0].map(v => arr.length > 1 ? loopOver(arr.slice(1), str + v) : str + v).flat()
console.log(loopOver(charSet))
Another implementation with ES6 recursive style
Array.prototype.cartesian = function(a,...as){
return a ? this.reduce((p,c) => (p.push(...a.cartesian(...as).map(e => as.length ? [c,...e] : [c,e])),p),[])
: this;
};
console.log(JSON.stringify([0,1].cartesian([0,1,2,3], [[0],[1],[2]])));

Categories