Reputation: 2588
I have a map of arrays of numbers in JavaScript. My goal is to get the key of the value that contains a certain number. I'm also open to a different data structure that might be more efficient.
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]
};
Each number will only ever appear once, but each array can contain close to a hundred numbers and it may grow substantially from there. The arrays could be immutable as my data is static.
Right now I have this, but it doesn't seem terribly efficient.
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;
}
}
Upvotes: 3
Views: 448
Reputation: 19090
lodash library has a lot of useful functions. Using it, you have the following options:
1. Binary search
Create a new structure with sorted array of numbers. When looking for a number, apply a binary search.
_.sortedIndexOf() method uses binary search in an array.
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"
Check the working demo.
2. Create a map object
Because the numbers will appear only once, it's easy to create a big hash object, where the key is the number and value is the category. It requires a bit more memory because copies the categories string, but it works quite fast.
This solution doesn't require 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"
Check the working demo.
Upvotes: 1
Reputation: 386654
I suggest to use a hashtable for the numbers.
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>');
Upvotes: 0
Reputation: 186
sort the array and use binary search. You can use lodash lib to do it easily.
Upvotes: 0
Reputation: 23863
There are too many what-ifs
to answer that question, the biggest one being: How often is the data going to be updated
vs read
.
If it is going to be read
much more often, then iterate through the bookCategory
first and create a sparse array/object that links the numbers back to the category.
(I'll go for object here):
// Generate this programatticly, of course.
bookCategoryLinkback = {
10064: "fantasy",
10066: "fantasy",
10071: "fantasy"
};
Upvotes: 0