【问题标题】:alternatives for excessive for() looping in javascriptjavascript 中过度 for() 循环的替代方案
【发布时间】:2011-06-25 21:03:13
【问题描述】:

情况

我目前正在编写一个在 html 元素中显示随机引用的 javascript 小部件。引号存储在 javascript 数组中,以及它们在 html 元素中显示的次数。要显示的报价不能与之前显示的报价相同。此外,选择报价的机会取决于它在 html 元素中的先前出现次数。 (与选择显示的其他引号相比,出现次数越少,机会越大。

当前解决方案

我目前已经通过使用大量循环遍历各种数组来使其工作(由于我严重缺乏 javascript 知识)。虽然这目前有效(!!)我发现这个解决方案对于我想要实现的目标来说相当昂贵。

我在寻找什么

  • 从数组中删除数组元素的替代方法,当前循环遍历整个数组以找到我要删除的元素并将所有其他元素复制到新数组中
  • 根据出现次数从数组中计算和选择元素的替代方法
  • 您注意到我应该/可以做的任何其他事情,同时仍然在情况下执行规定的业务规则

守则

var quoteElement = $("div#Quotes > q"),
    quotes = [[" AAAAAAAAAAAA ", 1],
              [" BBBBBBBBBBBB ", 1],
              [" CCCCCCCCCCCC ", 1],
              [" DDDDDDDDDDDD ", 1]],
    fadeTimer = 600,
    displayNewQuote = function () {

                        var currentQuote = quoteElement.text();
                        var eligibleQuotes = new Array();
                        var exclusionFound = false;
                        for (var i = 0; i < quotes.length; i++) {
                            var iteratedQuote = quotes[i];
                            if (exclusionFound === false) {
                                if (currentQuote == iteratedQuote[0].toString())
                                    exclusionFound = true;
                                else
                                    eligibleQuotes.push(iteratedQuote);
                            } else
                                eligibleQuotes.push(iteratedQuote);
                        }
                        eligibleQuotes.sort( function (current, next) {
                            return current[1] - next[1];
                        } );

                        var calculatePoint = eligibleQuotes[0][1];
                        var occurenceRelation = new Array();
                        var relationSum = 0;
                        for (var i = 0; i < eligibleQuotes.length; i++) {
                            if (i == 0)
                                occurenceRelation[i] = 1 / ((calculatePoint / calculatePoint) + (calculatePoint / eligibleQuotes[i+1][1]));
                            else
                                occurenceRelation[i] = occurenceRelation[0] * (calculatePoint / eligibleQuotes[i][1]);
                            relationSum = relationSum + (occurenceRelation[i] * 100);
                        }

                        var generatedNumber = Math.floor(relationSum * Math.random());
                        var newQuote;
                        for (var i = 0; i < occurenceRelation.length; i++) {
                            if (occurenceRelation[i] <= generatedNumber) {
                                newQuote = eligibleQuotes[i][0].toString();
                                i = occurenceRelation.length;
                            }
                        }

                        for (var i = 0; i < quotes.length; i++) {
                            var iteratedQuote = quotes[i][0].toString();
                            if (iteratedQuote == newQuote) {
                                quotes[i][1]++;
                                i = quotes.length;
                            }
                        }

                        quoteElement.stop(true, true)
                                    .fadeOut(fadeTimer);
                        setTimeout( function () {
                            quoteElement.html(newQuote)
                                        .fadeIn(fadeTimer);
                        }, fadeTimer);

                    } 

if (quotes.length > 1) 
    setInterval(displayNewQuote, 10000);

考虑的替代方案

  • 总是选择出现次数最少的数组元素。

    决定不这样做,因为这会/可能会在动画中显示过于明显的模式

  • 组合多个for循环以减少工作量

    决定不这样做,因为这会使代码变得深奥,下周我可能再也看不懂代码了

jsFiddle 参考

http://jsfiddle.net/P5rk3/

更新

用提到的技术重写了我的函数,虽然我担心这些技术仍然循环遍历整个数组以找到它的要求,至少我的代码看起来更干净:)

在这里阅读答案后使用的参考资料:

【问题讨论】:

标签: javascript jquery arrays sorting loops


【解决方案1】:

我建议大多数受支持的数组函数(如果不支持也可以轻松添加):

[].splice(index, howManyToDelete); // you can alternatively add extra parameters to slot into the place of deletion
[].indexOf(elementToSearchFor);
[].filter(function(){});

其他有用的函数包括forEachmap

我同意将所有工作组合成一个巨大的循环是丑陋的(而且并非总是可能的),而且这样做您收获甚少,因此可读性绝对是赢家。尽管这些数组函数不需要太多循环。

【讨论】:

  • 接受了这个答案,它最终引导我找到一个“更好”的解决方案。谢谢!
【解决方案2】:

你想要的答案:

创建一个整数数组来存储每个引用的使用次数。此外,还有一个全局变量 Tot,其中包含已使用的引号总数(即该整数数组的总和)。还可以找到Mean,如Tot / number of quotes

在 0 和 Tot - 1 之间选择一个随机数。

对于每个报价,添加Mean * 2 - the number of uses(*1)。当您得到该值已超过生成的随机数时,请选择该报价。

如果该报价是当前显示的报价,请选择下一个或上一个报价,或者只是重复该过程。

真正的答案:

使用随机引用,如果引用重复,则最多重复。当用户重新加载/离开页面时,数据使用将会丢失。而且,无论您如何巧妙地选择它们,大多数用户都不在乎。

(*1) 检查限制,即第一个或最后一个配额是否符合此公式。

【讨论】:

  • 关于 我想要的:在间隔之前显示 0 / 1 个引号时,该方法是否不会严重失败,tot(0 / 1) - 1 会解决为-1 / 0。关于真正的答案是的,我可能在这方面走得太远了,但我进入了真正想要解决这个问题的心态(尽可能完美地做到这一点) :)
【解决方案3】:

从数组中删除数组元素的替代方法

使用 ES5 的Array.filter() 方法:

Array.prototype.without = function(v) {
    return this.filter(function(x) {
        return v !== x;
    });
};

给定数组aa.without(v) 将返回a 的副本,其中不包含元素v

与选择显示的其他报价相比,出现次数越少,机会越高

你不应该与机会混为一谈——正如我的另一半数学家所说,“机会没有记忆”。

您的建议类似于彩票中尚未出现的数字必须“过期”,因此更有可能出现。这根本不是真的。

【讨论】:

  • 独立实验没有记忆;-)
  • 关于您的最新报价。我只是“创建”了这条规则,以减少不断查看报价 1 到 3 而报价 4 从未出现在您的屏幕上的几率。尽管在当前情况下仍可能发生这种情况,但可能性较小。所以就我而言,这些报价确实过期了。
  • @marcusklass true - 但每次调用 random() 有点像一个独立的实验 - 就像每次掷骰子或掷硬币一样。
【解决方案4】:

您可以编写函数来明确定义您要对循环执行的操作。

您的第一个循环是一个过滤器。 你的第二个循环是一张地图+一些副作用。 我不知道其他循环,它们很奇怪:P

过滤器类似于:

function filter(array, condition) {
    var i = 0, new_array = [];
    for (; i < array.length; i += 1) {
        if (condition(array[i], i)) {
            new_array.push(array[i]);
        }
    }
    return new_array;
}

var numbers = [1,2,3,4,5,6,7,8,9];
var even_numbers = filter(numbers, function (number, index) {
    return number % 2 === 0;
});
alert(even_numbers); // [2,4,6,8]

无法避免循环,但您可以通过创建一个解释您正在做什么的函数来向代码添加更多语义

【讨论】:

  • ES5 内置了Array.filter()
  • 是的,但还有其他循环操作不是。
【解决方案5】:

如果出于某种原因,您对 splicefilter 方法不满意,John Resig 提供了一个不错的(过时但仍然有效)方法:http://ejohn.org/blog/javascript-array-remove/

【讨论】:

    猜你喜欢
    • 2020-01-15
    • 1970-01-01
    • 2015-07-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-28
    相关资源
    最近更新 更多