选择 table 数据(对象数组)JS 的高性能方法

High performance methods for selecting table data (array of objects) JS

我正在尝试优化从大型 table(对象数组)中选择数据。

我想从一行中保存多个值,然后写入 localStorage。

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 作为数组中所有匹配的客户和联系人对,并且 cityid 也是唯一城市和 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);
    }
}

根据您获取数据的位置(如果您可以重构数据),您可以使用以客户、联系人或两者的某种组合作为键的哈希图(对象)在此搜索中获得更好的性能。

过滤器看起来不错;但是你可以 .

可以使用 Set and reduce:

优化获取唯一值
let id = [...selectedRow.reduce(
  (result,item)=>result.add(item.id)
  ,new Set()
)]

或者正如 Jonas 指出的那样(所以你不需要 reduce):

let id = [...new Set(
  selectedRow.map(item=>item.id)
)]
let customerTable = [
    {
        "customer": "Apple Computers",
        "contact": "Steve Jobs",
        "id": 1,
        "city": "Cupertino"
    },
    {
        "customer": "Microsoft",
        "contact": "Bill Gates",
        "id": 2,
        "city": "Redmond"
    },
    {
        "customer": "Microsoft",
        "contact": "Bill Gates",
        "id": 2,
        "city": "Redmond"
    }
]

let selectedCustomer = "Microsoft";
let selectedContact = "Bill Gates"; 

// [[id], [city]]
let results = customerTable.reduce((_i, _j) => { 
    if(!(_j.customer === selectedCustomer && _j.contact === selectedContact)) return _i;
    _i[0].push(_j.id);
    _i[1].push(_j.city);
    return _i; 
}, [[], []])
.map((v) => v.filter((i, j, a) => a.indexOf(i) === j));

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

但是您需要知道对于您的数据和 use-case,这种开销是否值得。