3

假设我有一个<select>元素:

<select id="foobar" name="foobar" multiple="multiple">
    <option value="1">Foobar 1</option>
    <option value="2">Foobar 2</option>
    <option value="3">Foobar 3</option>
</select>

假设我有一个值数组,例如:

var optionValues = [2, 3];

如何<option>最有效地选择值为 2 和 3 的 s?

我正在使用<select>具有数千个 s 的<option>s,因此像这样手动操作是行不通的:

var optionElements = [];

$("#foobar").children().each(function() {
    if($.inArray($(this).val(), optionValues)) {
        optionElements.push($(this));
    }
}

它太慢了。有没有办法让 jQuery 为我需要选择的元素提供一个值列表?有任何想法吗?

PS 如果您想知道,我正在优化我的jQuery PickList 小部件,该小部件目前在处理大型列表方面很糟糕

4

9 回答 9

3

您是否考虑过在插件引导程序中创建一个大哈希表?授予的值是唯一的:

var options = {};

$('#foobar').children().each(function(){

    options[this.value] = this;

});

这种查找方式很简单 - options[valueNeeded]

编辑 - 搜索optionValues

var optionValues = [2, 3];

var results = [];

for(i=0; i<optionValues.length;i++){

    results.push[ options[ optionValues[i] ] ];

}
于 2012-09-04T03:25:43.733 回答
2

这还没有被描述,所以把它和盐的振动筛一起拿来:

var options = $("some-select").children(),
    toFind = [2, 3],
    values = {},
    selectedValues = [],
    unSelectedValues = [];
// First, make a lookup table of selectable values
// O(1) beats O(n) any day
for (i=0, l=toFind.length; i++; i<l) {
    values[toFind[i]] = true;
}
// Avoid using more complicated constructs like `forEach` where speed is critical
for (i=0, l=options.length; i++; i<l) {
    // Avoid nasty edge cases since we need to support *all* possible values
    // See: http://www.devthought.com/2012/01/18/an-object-is-not-a-hash/
    if (values[options[i]] === true) {
        selectedValues.push(options[i]);
    }
    else {
        unSelectedValues.push(options[i]);
    }
}

显然我们可以做更多的事情(比如缓存选定和未选定的值,这样我们就可以避免每次用户在它们之间移动一个值时重新构建它们),如果我们假设数据都是唯一的,我们甚至可以将整个事情变成三个“哈希”——但无论我们做什么,我们都应该对其进行分析,确保它真的像我们想象的那样快。

于 2012-09-04T03:29:08.963 回答
1

假设这些值是唯一的,您可以采取一些捷径。例如,一旦你找到了一个值,你可以splice()通过将它从搜索数组中取出来停止搜索它。

不过,这将是最终的优化,将您从O(n^2)一路向下带到O(n log n):排序。

首先,遍历选项并构建一个数组。基本上你只想将 NodeList 转换为一个数组。然后,sort带有回调的数组以获取选项的值。对搜索数组进行排序。现在您可以遍历“选项”数组并查找当前最小的搜索项。

var optsNodeList = document.getElementById('foobar').options,
    optsArray = [], l = optsNodeList.length, i,
    searchArray = [2,3], matches = [], misses = [];
for( i=0; i<l; i++) optsArray[i] = optsNodeList[i];
optsArray.sort(function(a,b) {return a.value < b.value ? -1 : 1;});
searchArray.sort();
while(searchArray[0] && (i = optsArray.shift())) {
    while( i > searchArray[0]) {
        misses.push(searchArray.shift());
    }
    if( i == searchArray[0]) {
        matches.push(i);
        searchArray.shift();
    }
}
于 2012-09-04T03:11:00.470 回答
1

试试这个:

var $found = [];
var notFound = [];
var $opt = $('#foobar option');
$.each(optionValues, function(i, v){
    var $this = $opt.filter('[value='+v+']');
    if ($this.length) {
       $elems.push($this)
    } else {
       notFound.push(v);
    } 
})
于 2012-09-04T03:13:56.900 回答
1

首先,我要感谢大家的精彩回复!我正在考虑每一个,我可能会在做出决定之前做基准测试。

在此期间,我实际上根据对另一个问题的答案找到了一个“可接受的”解决方案。

这是我想出的(最后一个块,带有自定义filter()实现,是神奇发生的地方):

var items = self.sourceList.children(".ui-selected");

var itemIds = [];
items.each(function()
{
    itemIds.push( this.value );
});

self.element.children().filter(function()
{
    return $.inArray(this.value, itemIds) != -1;
}).attr("selected", "selected");

我怀疑这是否与你们发布的任何内容一样有效,但它已将 1500 个项目列表上的“添加”选项列表操作时间从大约 10 秒减少到 300 毫秒。

于 2012-09-04T03:48:22.667 回答
1

我会filter()尝试 jQuery 的方法,例如:

var matches = filter(function() {
    // Determine if "this" is a match and return true/false appropriately
});

// Do something with the matches
matches.addClass('foobar');

它可能不是这里最快的解决方案,但它已经相当优化并且非常简单,无需跟踪列表和所有爵士乐。对于您的情况,它应该足够快。

于 2013-04-14T23:31:35.393 回答
0

试试这个。

var optionValues = [2, 3],
    elements = [],
    options = document.getElementById('foobar').options;

var i = 0;
do {
    var option = options[i];
    if(optionValues.indexOf(+option.value) != -1) {
        elements.push(option);
    }
} while(i++ < options.length - 1);
于 2012-09-04T03:21:16.410 回答
0

让 optionValues 通过要选择的索引数组。

for(var i = 0; i < optionValues.length; i++) {
  document.forms[0].foobar.options[optionValues[i]].selected = true;
}
于 2012-09-04T04:19:38.750 回答
0

如果只想按值选择,下面的应该是合适的。它只循环选项一次,不调用任何其他函数,只有一个内置方法,所以它应该很快。

function selectMultiByValue(el, valuesArr) {

  var opts = el.options;
  var re = new RegExp('^(' + valuesArr.join('|') + ')$');

  // Select options
  for (var i=0, iLen=opts.length; i<iLen; i++) {
    opts[i].selected = re.test(opts[i].value);
  } 
}

在某些浏览器中,循环遍历集合很慢,因此首先将选项集合转换为数组可能是值得的。但是在这样做之前进行测试,这可能不值得。

请注意,如果选择不是多选,则只会选择具有最后列出的值的选项。

如果您想允许各种其他字符或大小写,您可能需要摆弄正则表达式。

于 2012-09-04T05:32:54.120 回答