对象数组中的二进制搜索 javascript
binary search in array of object javascript
我有一个对象数组:
array = [
{
name:'alex',
number:36783712773
},
{
name:'gabe',
number:99938873,
},
and so on like this
]
我知道二分查找的基本概念及其工作原理,但是互联网上的大多数示例都处理一个数字数组,所以我不知道如何按名称和 phone 通过数组进行搜索对象。
我可以从后端进行搜索,但我不想为 javascript 中可以轻松处理的事情进行不必要的 HTTP 调用。
提前致谢。
下面是二分查找的实现。
在此答案的先前版本中,生成测试用例时存在巨大错误。与线性搜索相比,更新版本显示了二进制搜索的优势,在使用小数组时已经如此。
这是一个脚本,用于计算对对象数组执行线性搜索和二分搜索的时间。首先是 10 个对象的数组,然后是 100、1000,最后是 10 000:
function binarySearch(array, target) {
let lo = 0, hi = array.length;
while (lo < hi) {
let mi = (lo + hi) >> 1;
let diff = target - array[mi].number;
if (diff === 0) return array[mi];
else if (diff < 0) hi = mi;
else lo = mi + 1;
}
}
function linearSearch(array, target) {
return array.find(o => o.number === target);
}
function test(searchFunc, array, searchValues, times) {
let accTime = 0;
for (let time = 0; time < times; time++) { // Time the execution several times
let now = performance.now();
for (let number of searchValues) {
let match = searchFunc(array, number);
if (!match) throw "err";
}
accTime += performance.now() - now;
}
return accTime / times; // ... and take average
}
for (let length = 10; length < 1e5; length *= 10)
{
let array = Array.from({length}, (_, number) =>
({ name: "alex", ismember: true, number: number * 100 + 13 })
);
let searchValues = Array.from({length: 10000}, () =>
Math.floor(length * Math.random()) * 100 + 13
);
// First a dry run
let linearSearchTime = test(linearSearch, array, searchValues, 2);
let binarySearchTime = test(binarySearch, array, searchValues, 2);
// The real run
linearSearchTime = test(linearSearch, array, searchValues, 5);
binarySearchTime = test(binarySearch, array, searchValues, 5);
console.log({length, binarySearchTime, linearSearchTime});
}
不同的可搜索属性
如果您想有时按一个 属性 使用二分搜索,有时按另一个 属性,那么您需要创建单独的“索引”来引用您的数据,每个索引都按相关 属性.
例如:
let data = [
{ name: "alex", number: 13 },
{ name: "helen", number: 10 },
{ name: "zoe", number: 11 },
{ name: "willy", number: 12 },
];
let index = Object.fromEntries(["name", "number"].map(prop => [
prop,
data.map(o => [o[prop], o])
.sort(([a], [b]) => (a > b) - (a < b))
]));
function binarySearch(array, target) {
let lo = 0, hi = array.length;
while (lo < hi) {
let mi = (lo + hi) >> 1;
let key = array[mi][0];
if (key === target) return array[mi][1]; // the actual data
else if (key > target) hi = mi;
else lo = mi + 1;
}
}
// demo
console.log(binarySearch(index.name, "willy"));
console.log(binarySearch(index.name, "zoe"));
console.log(binarySearch(index.name, "alex"));
console.log(binarySearch(index.name, "helen"));
console.log(binarySearch(index.number, 13));
console.log(binarySearch(index.number, 12));
console.log(binarySearch(index.number, 11));
console.log(binarySearch(index.number, 10));
我有一个对象数组:
array = [
{
name:'alex',
number:36783712773
},
{
name:'gabe',
number:99938873,
},
and so on like this
]
我知道二分查找的基本概念及其工作原理,但是互联网上的大多数示例都处理一个数字数组,所以我不知道如何按名称和 phone 通过数组进行搜索对象。
我可以从后端进行搜索,但我不想为 javascript 中可以轻松处理的事情进行不必要的 HTTP 调用。
提前致谢。
下面是二分查找的实现。
在此答案的先前版本中,生成测试用例时存在巨大错误。与线性搜索相比,更新版本显示了二进制搜索的优势,在使用小数组时已经如此。
这是一个脚本,用于计算对对象数组执行线性搜索和二分搜索的时间。首先是 10 个对象的数组,然后是 100、1000,最后是 10 000:
function binarySearch(array, target) {
let lo = 0, hi = array.length;
while (lo < hi) {
let mi = (lo + hi) >> 1;
let diff = target - array[mi].number;
if (diff === 0) return array[mi];
else if (diff < 0) hi = mi;
else lo = mi + 1;
}
}
function linearSearch(array, target) {
return array.find(o => o.number === target);
}
function test(searchFunc, array, searchValues, times) {
let accTime = 0;
for (let time = 0; time < times; time++) { // Time the execution several times
let now = performance.now();
for (let number of searchValues) {
let match = searchFunc(array, number);
if (!match) throw "err";
}
accTime += performance.now() - now;
}
return accTime / times; // ... and take average
}
for (let length = 10; length < 1e5; length *= 10)
{
let array = Array.from({length}, (_, number) =>
({ name: "alex", ismember: true, number: number * 100 + 13 })
);
let searchValues = Array.from({length: 10000}, () =>
Math.floor(length * Math.random()) * 100 + 13
);
// First a dry run
let linearSearchTime = test(linearSearch, array, searchValues, 2);
let binarySearchTime = test(binarySearch, array, searchValues, 2);
// The real run
linearSearchTime = test(linearSearch, array, searchValues, 5);
binarySearchTime = test(binarySearch, array, searchValues, 5);
console.log({length, binarySearchTime, linearSearchTime});
}
不同的可搜索属性
如果您想有时按一个 属性 使用二分搜索,有时按另一个 属性,那么您需要创建单独的“索引”来引用您的数据,每个索引都按相关 属性.
例如:
let data = [
{ name: "alex", number: 13 },
{ name: "helen", number: 10 },
{ name: "zoe", number: 11 },
{ name: "willy", number: 12 },
];
let index = Object.fromEntries(["name", "number"].map(prop => [
prop,
data.map(o => [o[prop], o])
.sort(([a], [b]) => (a > b) - (a < b))
]));
function binarySearch(array, target) {
let lo = 0, hi = array.length;
while (lo < hi) {
let mi = (lo + hi) >> 1;
let key = array[mi][0];
if (key === target) return array[mi][1]; // the actual data
else if (key > target) hi = mi;
else lo = mi + 1;
}
}
// demo
console.log(binarySearch(index.name, "willy"));
console.log(binarySearch(index.name, "zoe"));
console.log(binarySearch(index.name, "alex"));
console.log(binarySearch(index.name, "helen"));
console.log(binarySearch(index.number, 13));
console.log(binarySearch(index.number, 12));
console.log(binarySearch(index.number, 11));
console.log(binarySearch(index.number, 10));