Javascript 选择表数据(对象数组)的高性能方法
我试图优化从一个大表(一个对象数组)中选择数据 我想保存单行中的多个值,然后写入localStorageJavascript 选择表数据(对象数组)的高性能方法,javascript,arrays,mapreduce,Javascript,Arrays,Mapreduce,我试图优化从一个大表(一个对象数组)中选择数据 我想保存单行中的多个值,然后写入localStorage let customerTable = [ { "customer": "Apple Computers", "contact": "Steve Jobs", "id": 1, "city": "Cupertino" }, { "customer": "Microsoft", "contact": "Bill Gates",
let customerTable = [
{
"customer": "Apple Computers",
"contact": "Steve Jobs",
"id": 1,
"city": "Cupertino"
},
{
"customer": "Microsoft",
"contact": "Bill Gates",
"id": 2,
"city": "Redmond"
},
{
"customer": "Microsoft",
"contact": "Satya Nadella",
"id": 3,
"city": "Redmond"
}
]
let selectedRow = customerTable
.filter(i => { return i.customer === selectedCustomer })
.filter(i => { return i.contact === selectedContact })
let id = selectedRow
.map(a => a.id)
.filter((item, pos, self) => {return self.indexOf(item) === pos}) // Remove duplicates
let city = selectedRow
.map(a => a.city)
.filter((item, pos, self) => { return self.indexOf(item) === pos })
有没有一种更有效的方法可以从这种类型的数据模型中选择多个值?一般来说,您希望减少循环的数量,因此当一个循环可以获得相同的结果时,不应该使用多个数组操作。可以优化您正在执行的操作
let selectedRow = customerTable
.filter(i => { return i.customer === selectedCustomer })
.filter(i => { return i.contact === selectedContact });
在数组中循环两次。它可以重写为仅在数组中循环一次
let selectedRow = customerTable
.filter(i => {return i.customer === selectedCustomer && i.contact === selectedContact});
另一个示例还利用可以在一个循环中执行的多个数组操作
您当前的代码将计算selectedRow
,因为数组和city
和id
中的所有匹配客户和联系人对也是唯一城市和id的数组。这可以在单个循环中执行
// using Set for performance as suggested by @HMR
let selectedRows = [], cities = new Set(), ids = new Set();
for (let i = 0; i = customerTable.length; i++) {
if (customerTable[i].customer === selectedCustomer && customerTable[i].contact === selectedContact) {
selectedRows.push(customerTable[i]);
// include uniqueness contraint on cities and ids
cities.add(customerTable[i].city);
ids.add(customerTable[i].id);
}
}
如果可以重构数据,根据您获取数据的位置,您可以使用hashmap(对象),以customer、contact或两者的某种组合作为关键字,从而获得更好的搜索性能。过滤器看起来很好;不管你怎么做 可以使用和优化获取唯一值: 或者正如乔纳斯指出的(所以你不需要减少): 最后一个
.map
,.filter
用于删除重复项(如问题所述),如果您确定不会有任何重复项,则可以删除此行
如果不需要其他筛选,也不需要重复,那么代码将如下所示
let results = customerTable.reduce((_i, _j) => {
if(!(_j.customer === selectedCustomer && _j.contact === selectedContact)) return _i;
_i[0] = _j.id;
_i[1] = _j.city;
return _i;
}, [-1, ""])
首先,为什么有重复的ID?ID的意义不在于它们是唯一的吗
除了优化实际代码外,还可以优化正在筛选的数据;正如评论中提到的,搜索短列表比搜索长列表更快 因此,如果与对数据进行的搜索相比,数组的更改相对较少,那么创建一个或多个索引可能是值得的。这可以简单到:
let ixCustomer = new Map();
const ixCustomerKey = item => item.customer.charAt(0).toLowerCase();
const add = (index, key, value) => {
if(index.has(key)) index.get(key).push(value)
else index.set(key, [value]));
}
for(let item of customerTable){
add(ixCustomer, ixCustomerKey(item), item);
}
因此,如果您进行搜索,则不必搜索customerTable
,而只需搜索一个子集,如果您选择正确的方法对数据进行索引,该子集应该比原始数组小得多
let id = new Set();
let city = new Set();
let arr = ixCustomer.get(ixCustomerKey(selectedCustomer)) || [];
for(let item of arr){
if(item.customer !== selectedCustomer || item.company !== selectedCompany) continue;
id.add(item.id);
city.add(item.city);
}
但您需要知道,对于您的数据和您的用例来说,这种开销是否值得。您将四个数组操作链接在一起。您可以将它们组合在一起,因此只需对数组进行一次检查,而不是四次。这可以手动(将代码混合在一起)、半手动(将函数组合在一起)、自动处理(惰性计算、传感器)。但是,很难说什么是最有效的方法,这还取决于代码库以及您是否愿意使用其他库或自己实现一些功能以及其他因素。要扩展@VLAZ,可以让selectedRow=customerTable.filter(i=>{return i.customer==selectedCustomer})。filter(i=>{return i.contact===selectedContact})不能作为AND操作写入。筛选器(i=>{return i.customer===selectedCustomer&&i.contact===selectedContact})(仅作为示例。您可能可以对所有链接循环执行相同的操作。)@CalIrvine只需在一个
中执行每一个操作。reduce
,这样您就可以将数组总共检查两次,而不是六次。您甚至可以通过同时执行id
和city
逻辑并将项分配给两个不同的数组,将其压缩为一次运行。@CalIrvine对和操作。@VLAZ您能告诉我reduce模式是什么样子吗?someArray.filter().filter()
不会在someArray中循环两次。第一个过滤器的结果会给第二个过滤器,所以您没有用“优化”进行太多优化@HMR优化消除了对过滤器的第二次调用的所有循环,并减少了函数调用的开销。问题表明,在高性能代码中,即使是很小的时间节约也很重要。我认为OP希望过滤大数组,并从过滤结果中获得唯一的ID和城市。最大的收益是当尝试从(可能非常大)获取唯一值时,可能不使用indexOf筛选结果。优化获取唯一值的一种方法是使用reduce with Set。此外,我认为更重要的是编写性能足够好的可组合/可重用代码,因此这不是一个明显的问题。@HMR很好的一点,我从问题中得出的假设是,OP需要性能良好的代码,即使以可读性和可重用性为代价但是我可能完全错了。我更新了我的答案以使用Set。有了足够的搜索,最佳解决方案可能是将整个数组放在一个映射中,然后进行大量快速搜索。我同意您的主要观点,即优化重复筛选是这里要做的事情,但我只想做[…new Set(selectedRow.Map(it=>it.id))]
@jonaswillms您是对的,提供一个数组来设置构造函数会更简单、更容易阅读。
let results = customerTable.reduce((_i, _j) => {
if(!(_j.customer === selectedCustomer && _j.contact === selectedContact)) return _i;
_i[0] = _j.id;
_i[1] = _j.city;
return _i;
}, [-1, ""])
let ixCustomer = new Map();
const ixCustomerKey = item => item.customer.charAt(0).toLowerCase();
const add = (index, key, value) => {
if(index.has(key)) index.get(key).push(value)
else index.set(key, [value]));
}
for(let item of customerTable){
add(ixCustomer, ixCustomerKey(item), item);
}
let id = new Set();
let city = new Set();
let arr = ixCustomer.get(ixCustomerKey(selectedCustomer)) || [];
for(let item of arr){
if(item.customer !== selectedCustomer || item.company !== selectedCompany) continue;
id.add(item.id);
city.add(item.city);
}