【问题标题】:Most efficient way to search a map of arrays in JavaScript在 JavaScript 中搜索数组映射的最有效方法
【发布时间】:2016-08-14 01:45:23
【问题描述】:

我在 JavaScript 中有一个数字数组的映射。我的目标是获取包含某个数字的值的键。我也对可能更有效的不同数据结构持开放态度。

let bookCategory = {
    "fantasy": [10064, 10066, 10071],
    "scifi": [10060, 10037, 10061],
    "history": [10001, 10003, 10004, 10005],
    "biography": [10032, 10006, 10002, 10028, 10009, 10030, 100031],
    "educational": [10025]
};

每个数字只会出现一次,但每个数组可以包含近百个数字,并且可能会从那里大幅增长。数组可能是不可变的,因为我的数据是静态的。

现在我有这个,但它似乎效率不高。

let category;
let keys = _.keys(categories);
let theNumber = 10032;

for(let j = 0; j < keys.length; j++) {
    if(_.includes(categories[keys[j]], theNumber)) {
        category = keys[j];
        break;
    }
}

【问题讨论】:

  • 10001300 的输出应该是什么?
  • @SalvadorDali 这些数字不在任何数组中,因此类别将保持为undefined
  • 如果它们不同 - 创建从值到类别的映射。
  • 一个数字可以属于多个类别吗?
  • @NinaScholz 不。如果有的话,每个数字只会出现一次。

标签: javascript arrays algorithm


【解决方案1】:

lodash 库有很多有用的功能。使用它,您有以下选择:

1.二进制搜索

创建一个带有排序数组的新结构。查找数字时,应用二进制搜索。
_.sortedIndexOf() 方法在数组中使用二进制搜索。

var bookCategory = {
 "fantasy": [10064, 10066, 10071],
 "scifi": [10060, 10037, 10061],
 "history": [10001, 10003, 10004, 10005],
 "biography": [10032, 10006, 10002, 10028, 10009, 10030, 100031],
 "educational": [10025]
};

var binaryMap = _.mapValues(bookCategory, function(category) {
  return category.sort(function(num1, num2) { 
    return num1 - num2; 
  });
});

//then search using binary algorithm    
var number = 10032;
var keyForNumber = _.findKey(binaryMap, function(numbers) {
  return _.sortedIndexOf(numbers, number) !== -1;
});

keyForNumber // prints "biography"

检查工作的demo

2。创建地图对象

因为数字只会出现一次,所以很容易创建一个大哈希对象,其中键是数字,值是类别。它需要更多的内存,因为它会复制类别字符串,但它的运行速度非常快。
此解决方案不需要 lodash。

var bookCategory = {
 "fantasy": [10064, 10066, 10071],
 "scifi": [10060, 10037, 10061],
 "history": [10001, 10003, 10004, 10005],
 "biography": [10032, 10006, 10002, 10028, 10009, 10030, 100031],
 "educational": [10025]
};

var map = _.reduce(bookCategory, function(result, numbers, key) {
  _.each(numbers, function(number) {
    result[number] = key;
  });
  return result;
}, {});

// or alternative without lodash
var mapAlternative = Object.keys(bookCategory).reduce(function(result, key) {
  bookCategory[key].forEach(function(number) {
    result[number] = key;
  });
  return result;
}, {});


var number = 10003;
map[number]; // prints "history"

检查工作的demo

【讨论】:

  • 第二个命题不需要Lodash。
【解决方案2】:

有太多的what-ifs 无法回答这个问题,最大的问题是:updatedread 的数据将多久出现一次。

如果要更频繁地使用read,则首先遍历bookCategory,并创建一个将数字链接回类别的稀疏数组/对象。

(我会在这里找对象):

// Generate this programatticly, of course.
bookCategoryLinkback = {
    10064: "fantasy",
    10066: "fantasy",
    10071: "fantasy"
};

【讨论】:

  • 我的数组是完全静态的。如果我使用Immutable 之类的东西,我可以完全冻结它们。根本没有更新。
  • 然后我会使用一个循环,类似于你所拥有的,然后像上面显示的那样创建一个新的反向引用对象。
【解决方案3】:

对数组进行排序并使用二分查找。您可以使用 lodash lib 轻松完成。

【讨论】:

  • 排序不会比简单地遍历它们寻找答案花费更长的时间吗?或者是排序比我想象的更快。
  • @diplosaurus 我们不知道你在想什么。
【解决方案4】:

我建议对数字使用哈希表。

var bookCategory = {
        "fantasy": [10064, 10066, 10071],
        "scifi": [10060, 10037, 10061],
        "history": [10001, 10003, 10004, 10005],
        "biography": [10032, 10006, 10002, 10028, 10009, 10030, 100031],
        "educational": [10025]
    },
    numbers = function (data) {
        var r = Object.create(null);
        Object.keys(data).forEach(k => data[k].forEach(a => r[a] = k));
        return r;
    }(bookCategory)

document.write('<pre>' + JSON.stringify(numbers, 0, 4) + '</pre>');

【讨论】:

    猜你喜欢
    • 2021-10-08
    • 2018-07-13
    • 2014-10-31
    • 2011-06-06
    • 1970-01-01
    • 2020-11-25
    • 1970-01-01
    • 2014-04-11
    相关资源
    最近更新 更多