交换数组中除 first 和 last 之外的所有元素

Swapping all elements of an array except for first and last

我有一个看起来像这样的数组

const x = ['A','B','C','D','E']

我想要一个优雅的函数来随机排列数组的内容,但保持第一个或最后一个元素固定。像 customShuffle(x) 这样的东西会打乱数组,但确保元素 "A" 位于第一个位置,元素 "E" 位于最后一个位置。所有其他元素都被打乱。

    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;
    }

您可以使用此函数,它使用 the modern version of the Fisher–Yates shuffle algorithm 来打乱子数组 x.slice(1, x.length - 1),即 x 排除第一个和最后一个元素,然后将它们添加回去到打乱后的子数组:

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));

使用 How to randomize (shuffle) a JavaScript array?

中的随机播放算法

你可以这样扩展它:

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);
}

你可以这样做。 firstlast 参数是可选的。

检查first是否通过,是否在数组中。如果是这样,则将其从数组中删除。对 last 执行相同的操作。打乱剩余数组的索引。根据打乱的索引以及 firstlast 参数重新创建新数组。

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); 将随机播放整个数组。
arr = shuffle(arr, 'A'); 会将 A 移到前面,然后将其余的洗牌。
arr = shuffle(arr, 'A', 'B'); 会将 A 移到前面,B 移到最后,然后将其余的洗牌。

注意事项:虽然这种方法不合适,但由于 splice 方法,它仍会改变原始数组。

您可以先生成新的随机排列数组,然后检查是否提供了第一个和最后一个参数,然后将这些元素放在第一个和最后一个位置。

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"))

请尝试以下简单的 solution.This 将随机排列除数组的第一个和最后一个元素 (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);
}

如果 first 和 last 元素事先没有到位,您可以尝试以下 (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;
}

延伸阅读:

如果数组的第一个和最后一个元素始终位于同一个位置,您可以应用正常的改组算法,如 Fisher and Yates' 的现代变体,跳过这些位置:

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;
}

否则,如果您想选择第一个和最后一个元素,正如您在原始问题中指出的那样,您可以这样做:

  1. 首先在第一个和最后一个位置找到你想要的元素的索引:firstIndexlastIndex
  2. 如果这些元素存在(它们可能不存在),将它们从数组中移除。
  3. 对剩余元素应用洗牌算法(不需要洗牌 firstlast)。
  4. 如果需要,将第一个和最后一个元素添加回它们的位置。

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;
}

尝试这样的事情。它保留第一个和最后一个元素而不明确定义它们的值,并构建一个新数组,其他元素随机排列。

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;
}

因为你要求优雅,所以我喜欢在这里实现更函数式的编程风格。下面的代码做你想要的。你用你的数组来补充 shuffle 函数,你希望它洗牌的最大次数(数字越大,洗牌越好),以及 true 来保持第一个元素,false保留最后一个。

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]);
}

用法示例:

shuffle(['A','B','C','D','E'], 10, true);

输出:["A", "C", "D", "B", "E"]

我希望这就是您正在寻找的并能回答您的问题。

编辑

事实证明,您可以在 一个 行中获得全部随机播放逻辑(删除不必要的换行符时)。当您添加这两行以保留第一个或最后一个字符时,您基本上可以使用 行代码创建此函数。