【问题标题】:Javascript Shuffle Array from Txt File来自 Txt 文件的 Javascript 随机播放数组
【发布时间】:2016-03-04 22:29:44
【问题描述】:

我希望从文本文件中提取 5 行“随机”行,而不是重复任何行。文本文件中的每一行都有 html 代码,这些代码将被插入到侧面菜单中。我已经阅读了 Fisher-Yates shuffle 但不确定如何以这种方式将它与 javascript 结合起来。目前我有以下抛出错误:

var request = new XMLHttpRequest();
request.onload = function() {
    var i = 0;
    // get the file contents
    var fileContent = this.responseText;
    // split into lines
    var fileContentLines = fileContent.split( '\n' );

    var target = document.getElementById( 'random-testimonial' );
    var targetHTML = target.innerHTML;

    while ( i < 5 ) {
        // get a random index (line number)
        var randomLineIndex = Math.floor( Math.random() * fileContentLines.length );
        // extract the value
        var randomLine = fileContentLines[ randomLineIndex ];

        // add the random line in a div if not duplicate            
        if ( ! targetHTML.contains(randomLine) ) {
            targetHTML += randomLine;
            i += 1;
        }
    }

    target.innerHTML = targetHTML;
};
request.open( 'GET', 'content.txt', true );
request.send();

<div id="random-content"><script src="content.js"></script></div>

错误:

content.js:19 Uncaught TypeError: targetHTML.contains is not a functionrequest.onload @ content.js:19

【问题讨论】:

  • 尝试在if 条件下用.indexOf() 替换.contains()

标签: javascript arrays random text-files shuffle


【解决方案1】:

好的,Fisher-Yates shuffle 的工作方式是

  • 从输入数组中获取随机索引r
  • 将元素r从输入数组复制到输出数组
  • 从输入数组中移除元素r
  • 重复n 次,其中n 是输入数组的长度

在循环结束时,输出将是整个输入数组的随机副本。

我将回顾一个小问题:该算法不应该改变输入数组。相反,它应该保持输入数组不变并返回一个新数组,该数组是输入数组的洗牌副本。 (您可以在下面的实现中看到这是如何完成的)。

因此,了解 Fisher-Yates 的工作原理后,就您而言,我们不必洗牌 整个 数组,因为您事先知道您只需要 N 元素。

让我们先看看你的输入。

var fileContent = this.responseText;
var fileContentLines = fileContent.split( '\n' );

好的,完美。您已经定义了输入数组fileContentLines。现在让我们创建一个函数来从中采样一些随机元素

// fisher-yates sample
// • sample n elements from xs
// • does not mutate xs
// • guarantees each sampled element is unique
function sample (n,xs) {
  function loop(i, output, input, len) {
    if (i === n) return output;                   // loop exit condition
    let r = Math.floor(Math.random() * len);      // rand number between 0 and len
    return loop(                                  // continue loop
      i + 1,                                      // increment loop counter
      output.concat(input[r]),                    // copy element r from input
      input.slice(0,r).concat(input.slice(r+1)),  // remove element from input
      len - 1                                     // decrement length
    );
  }
  return loop(0, [], xs, xs.length);              // begin loop
}

好吧!我们先用一个简单的输入检查一下

// sample 3 random inputs from numbers 1 through 10
console.log(sample(3, [1,2,3,4,5,6,7,8,9,10])); //=> [9,7,5]

完美。现在只需在你的行数组上调用它

var result = sample(5, fileContentLines);
console.log(result); // ...

上面的代码有效,但我们不要停在这里。我们的代码承担了太多的责任,我们可以将一些行为分离成可重用的函数。

// get rand number between 0 and n
function rand(x) {
  return Math.floor(Math.random() * x);
}

// splice of xs at index i
// • return a new output array
// • does not mutate xs
function del(i,xs) {
  return xs.slice(0,i).concat(xs.slice(i+1));
}

// fisher-yates sample
// • sample n elements from xs
// • does not mutate xs
// • guarantees each sampled element is unique
function sample (n,xs) {
  function loop(i, output, input, len) {
    if (i === n) return output;       // loop exit condition
    let r = rand(len);                // rand number between 0 and len
    return loop(                      // continue loop
      i + 1,                          // increment loop counter
      output.concat(input[r]),        // copy element r from input
      del(r,input),                   // remove element from input
      len - 1                         // decrement length
    );
  }
  return loop(0, [], xs, xs.length);  // begin loop
}

// fisher-yates shuffle
// • does not mutate xs
function shuffle(xs) {
  return sample(xs.length, xs);
}

让我们快速浏览一下每个函数的个别行为

// generate random number between 0 and 10 (exclusive)
console.log(rand(10)); //=> 5

// delete 2nd letter from letters a through d
console.log(del(1, ['a', 'b', 'c', 'd'])); // => ['a', 'c', 'd]

// sample 3 random inputs from numbers 1 through 10
console.log(sample(3, [1,2,3,4,5,6,7,8,9,10])); //=> [9,7,5]

// shuffle entire input array
console.log(shuffle([1,2,3,4,5,6,7,8,9,10])); //=> [8,9,1,3,7,6,10,5,4,2]

你有它:4 个功能,价格为 1。在我看来,这是解决问题的一种更好的方法,因为每个功能都可以单独使用,因此可以在多个地方使用。拥有大量可重用的小功能将大大减少您将来要做的工作量。


很好地划分了所有这些复杂性,让我们看看您的最终代码可能是什么样子。

function createParagraph(text) {
  var p = document.createElement('p');
  p.innerHTML = text;
  return p;
}

var request = new XMLHttpRequest();
request.onload = function() {

  var fileContent = this.responseText;
  var fileContentLines = fileContent.split('\n');
  var target = document.getElementById('random-testimonial');

  sample(5, fileContentLines).map(function(testimonial) {
    var p = createParagraph(testimonial);
    target.appendChild(p);
  });
};
request.open('GET', 'content.txt', true);
request.send();

PS 我强烈建议您为您的 ajax 请求编写可重用函数,或者使用库更好。手写它们非常麻烦且容易出错。大多数人使用jQuery,但最近我一直在使用axios

【讨论】:

  • @SirajHussain wups,我有一个小错字^_^。我有.splice 我打算.slice。感谢您的鹰眼。
  • 非常感谢您花时间回复 naomik。我不是代码编写者,你的回答对我来说有点淹没,因为我只需要一个简单的脚本解决方案,它不会经常使用,可以轻松集成。输入您收到的代码: content.js:14 Uncaught ReferenceError: sample is not definedrequest.onload @ content.js:14
  • 是的,所以你做错了。好吧,这是浪费精力。经验教训。
【解决方案2】:
var request = new XMLHttpRequest();
request.onload = function() {
    var i = 0;
    // get the file contents
    var fileContent = this.responseText;
    // split into lines
    var fileContentLines = fileContent.split( '\n' );

    var target = document.getElementById( 'random-testimonial' );

    var HTMLLines = [];

    while ( i < 5 ) {
        // get a random index (line number)
        var randomLineIndex = Math.floor( Math.random() * fileContentLines.length );
        // extract the value
        var randomLine = fileContentLines[ randomLineIndex ];

        // add the random line if not duplicate            
        if ( HTMLLines.indexOf(randomLine) === -1) {
            HTMLLines.push(randomLine);
            i += 1;
        }
    }

    target.innerHTML = HTMLLines.join('\n');
};
request.open( 'GET', 'content.txt', true );
request.send();

【讨论】:

  • Fisher-Yates 不进行重复/唯一检查。
  • @naomik 请重读他的问题:我希望从文本文件中提取 5 行“随机”行而不重复任何行。我的解决方案解决了他的错误,并通过对代码的最小更改来完成他的问题。 Fisher-Yates 只是他正在考虑实施,但尚未开始实施。
  • 您可以随机抽取 5 行,而无需进行不必要的重复/唯一检查。如果原始代码不好,“对他的代码进行最小的更改”就不是一个值得的目标。这种方法是懒惰的,imo。原始代码在这里需要大量工作,以至于几乎每一行 行都应该重新编写。是的,解释为什么需要重做需要花费大量精力,但是帮助初学者编写糟糕的代码或保留他们已经编写的糟糕代码并没有帮到任何忙.
  • 对寻求他人帮助的人缺乏努力感到非常失望。
  • @naomik 在下次发帖之前尝试了解某人的需求。我有兴趣帮助某人解决他们手头的问题,您对互联网点感兴趣
猜你喜欢
  • 1970-01-01
  • 2011-01-27
  • 2013-09-27
  • 2019-04-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多