最有效的方法是用一系列值来选择所有元素。

时间:2022-06-17 21:42:42

Let's suppose I have a <select> element:

假设我有一个

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

And let's suppose I have an array of values, something like:

假设我有一个数组的值,比如

var optionValues = [2, 3];

How can I select the <option>s with values 2 and 3 most efficiently?

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

I'm working with a <select> that has thousands of <option>s, so doing it manually like this won't work:

我使用的是

var optionElements = [];

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

It's just too slow. Is there a way to hand jQuery a list of values for the elements I need to select? Any ideas?

太缓慢。是否有一种方法可以让jQuery为我需要选择的元素提供一个值列表?什么好主意吗?

P.S. In case you're wondering, I am in the middle of optimizing my jQuery PickList widget which currently sucks at handling large lists.

如果你想知道,我正在优化我的jQuery PickList小部件,它目前在处理大型列表上很糟糕。

9 个解决方案

#1


3  

Have you considered creating a big hashtable at plugin bootstrap? Granted values are unique:

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

var options = {};

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

    options[this.value] = this;

});

This way looking up is straightforward - options[valueNeeded].

这种向上查找的方法是简单的-选项[valueNeeded]。

EDIT - searching for optionValues:

编辑-搜索optionValues:

var optionValues = [2, 3];

var results = [];

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

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

}

#2


2  

This hasn't been profiled so take it with a grain shaker of salt:

这还没有被证实,所以要用一种盐调味剂:

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

There is obviously more we can do (like caching the selected and unselected values so we can avoid rebuilding them every time the user moves a value between them) and if we assume that the data is all unique we could even turn the whole thing into three "hashes" - but whatever we do we should profile it and ensure that it really is as fast as we think it is.

显然是有我们可以做(比如缓存选择并没有值,这样我们就可以避免重建他们每次用户移动它们之间的一个值),如果我们假设数据都是独特的,我们甚至可以把整个事情变成三个“散列”——但无论我们做什么我们应该配置文件,并确保它真的是我们认为这是一样快。

#3


1  

Assuming the values are unique, you can take some shortcuts. For instance, once you have found a value you can stop searching for it by splice()ing it off the search array.

假设值是唯一的,可以使用一些快捷方式。例如,一旦您找到了一个值,您可以停止通过splice()将它从搜索数组中搜索出来。

This would be the ultimate optimisation, though, taking you from O(n^2) all the way down to O(n log n): Sorting.

这将是最终的优化,把你从O(n ^ 2)一直到O(n log n):排序。

First, loop through the options and build an array. Basically you just want to convert the NodeList to an Array. Then, sort the array with a callback to fetch the option's value. Sort the search array. Now you can loop through the "options" array and look for the current smallest search item.

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

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

#4


1  

Try this:

试试这个:

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

#5


1  

First of all, I want to thank you all for the awesome responses! I'm considering each one, and I will probably do benchmarks before I make a decision.

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

In the interim, I actually found an "acceptable" solution based on this answer to another question.

在此期间,我实际上找到了一个“可接受的”解决方案,基于这个答案来回答另一个问题。

Here's what I came up with (the last block, with the custom filter() implementation, is where the magic happens):

下面是我所提出的(最后一个块,带有自定义过滤器()实现,是魔术发生的地方):

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

I doubt this is as efficient as any of the stuff you guys posted, but it has decreased the "Add" picklist operation time from about 10 seconds to 300ms on a 1500 item list.

我怀疑这是否和你们发布的任何东西一样有效,但是它减少了“添加”的“选择”操作时间,从大约10秒到300毫秒,在1500个项目列表中。

#6


1  

I would give jQuery's filter() method a try, something like:

我可以尝试一下jQuery的filter()方法,比如:

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

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

It may not be the fastest solution here, but it is fairly optimized and very very simple without having to keep track of lists and all that jazz. It should be fast enough for your situation.

它可能不是这里最快的解决方案,但它是相当优化的,而且非常简单,不需要跟踪列表和所有的jazz。你的情况应该够快的了。

#7


0  

Try this.

试试这个。

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

#8


0  

Let optionValues by an array of indexes to be selected.

让optionValues由一系列的索引选择。

for(var i = 0; i < optionValues.length; i++) {
  document.forms[0].foobar.options[optionValues[i]].selected = true;
}

#9


0  

If you just want to select by value, the following should be suitable. It only loops over the options once and doesn't call any other functions, only one built–in method so it should be quick.

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

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

In some browsers, looping over a collection is slow so it may pay to convert the options collection to an array first. But test before doing that, it may not be worth it.

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

Note that if the select isn't a multiple select, only the option with the last listed value will be selected.

注意,如果select不是多个select,那么只选择具有最后一个列出值的选项。

You may need to fiddle with the regular expression if you want to allow various other characters or cases.

如果您希望允许其他字符或案例,您可能需要修改正则表达式。

#1


3  

Have you considered creating a big hashtable at plugin bootstrap? Granted values are unique:

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

var options = {};

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

    options[this.value] = this;

});

This way looking up is straightforward - options[valueNeeded].

这种向上查找的方法是简单的-选项[valueNeeded]。

EDIT - searching for optionValues:

编辑-搜索optionValues:

var optionValues = [2, 3];

var results = [];

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

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

}

#2


2  

This hasn't been profiled so take it with a grain shaker of salt:

这还没有被证实,所以要用一种盐调味剂:

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

There is obviously more we can do (like caching the selected and unselected values so we can avoid rebuilding them every time the user moves a value between them) and if we assume that the data is all unique we could even turn the whole thing into three "hashes" - but whatever we do we should profile it and ensure that it really is as fast as we think it is.

显然是有我们可以做(比如缓存选择并没有值,这样我们就可以避免重建他们每次用户移动它们之间的一个值),如果我们假设数据都是独特的,我们甚至可以把整个事情变成三个“散列”——但无论我们做什么我们应该配置文件,并确保它真的是我们认为这是一样快。

#3


1  

Assuming the values are unique, you can take some shortcuts. For instance, once you have found a value you can stop searching for it by splice()ing it off the search array.

假设值是唯一的,可以使用一些快捷方式。例如,一旦您找到了一个值,您可以停止通过splice()将它从搜索数组中搜索出来。

This would be the ultimate optimisation, though, taking you from O(n^2) all the way down to O(n log n): Sorting.

这将是最终的优化,把你从O(n ^ 2)一直到O(n log n):排序。

First, loop through the options and build an array. Basically you just want to convert the NodeList to an Array. Then, sort the array with a callback to fetch the option's value. Sort the search array. Now you can loop through the "options" array and look for the current smallest search item.

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

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

#4


1  

Try this:

试试这个:

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

#5


1  

First of all, I want to thank you all for the awesome responses! I'm considering each one, and I will probably do benchmarks before I make a decision.

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

In the interim, I actually found an "acceptable" solution based on this answer to another question.

在此期间,我实际上找到了一个“可接受的”解决方案,基于这个答案来回答另一个问题。

Here's what I came up with (the last block, with the custom filter() implementation, is where the magic happens):

下面是我所提出的(最后一个块,带有自定义过滤器()实现,是魔术发生的地方):

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

I doubt this is as efficient as any of the stuff you guys posted, but it has decreased the "Add" picklist operation time from about 10 seconds to 300ms on a 1500 item list.

我怀疑这是否和你们发布的任何东西一样有效,但是它减少了“添加”的“选择”操作时间,从大约10秒到300毫秒,在1500个项目列表中。

#6


1  

I would give jQuery's filter() method a try, something like:

我可以尝试一下jQuery的filter()方法,比如:

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

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

It may not be the fastest solution here, but it is fairly optimized and very very simple without having to keep track of lists and all that jazz. It should be fast enough for your situation.

它可能不是这里最快的解决方案,但它是相当优化的,而且非常简单,不需要跟踪列表和所有的jazz。你的情况应该够快的了。

#7


0  

Try this.

试试这个。

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

#8


0  

Let optionValues by an array of indexes to be selected.

让optionValues由一系列的索引选择。

for(var i = 0; i < optionValues.length; i++) {
  document.forms[0].foobar.options[optionValues[i]].selected = true;
}

#9


0  

If you just want to select by value, the following should be suitable. It only loops over the options once and doesn't call any other functions, only one built–in method so it should be quick.

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

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

In some browsers, looping over a collection is slow so it may pay to convert the options collection to an array first. But test before doing that, it may not be worth it.

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

Note that if the select isn't a multiple select, only the option with the last listed value will be selected.

注意,如果select不是多个select,那么只选择具有最后一个列出值的选项。

You may need to fiddle with the regular expression if you want to allow various other characters or cases.

如果您希望允许其他字符或案例,您可能需要修改正则表达式。