问题
I'm using Immutable.js with Redux in my React Native app.
Metadata, such as lookup tables, are fetched from a server and persisted locally in the app as Immutable.Map. The keys for the lookup values are integers (the primary key in the db).
When I fetch the data all integer keys are coerced into strings. This is normal behaviour for js objects. Thus, when a lookup Map is created, the keys will be strings.
Example:
let colors = Immutable.Map({
'10': 'yellow',
'20': 'pink',
..
})
The objects that are referencing the lookup values has the reference stored as numeric like this:
let michael = Immutable.Map({
name: 'Michael',
colorId: 10
})
Since Immutable.js does not coerce numeric keys to string I cannot do this:
let color = colors.get(michael.get('colorId'))
The above is the same as:
let color = colors.get(10)
This won't work, because the id is a string. This would work:
let color = colors.get('10')
I usually use Immutable.List to store datasets fetched from the server. To get an item I use find(). But the small lookup tables (essentially app metadata) are accessed very often and need to be speedy.
How do you solve this? Here are some alternatives to consider:
1. Manually convert keys to strings when searching
let color = colors.get(michael.get('colorId') + '')
cons:
- Not an attractive solution. Seems error prune to me.
2. Use plain js objects for lookup lists
pros:
- Object key lookup are fast in js
- Automatic coercion of keys to strings, both ways (writing and reading)
cons:
- Ideally I'd like to use Immutable.js everywhere.
3. Use List.find() for lookup tables
pros:
- Same concept as for the big datasets
cons:
- I don't know the performance impact of using List.find() instead of Map.get()
4. Carefully craft the Immutable.Map to use numeric key
let colors = new Immutable.Map()
.set(10, 'yellow')
.set(20, 'pink')
pros:
- Keys in the maps will match the value referencing the key (i.e. both are numeric)
cons:
- cumbersome
- Keys are already coerced to strings in the json object sent over the wire, so I'd essentially be converting them back to numeric (I think)
Edit.. did some performance tests
Interesting data from a simple performance test.
- Lookup list of 10 items
- Find a value one million times
- Mac mini core i7 2.6
Immutable.Map with numeric keys: 185 ms
Immutable.List using find(): 972 ms
Plain JS object with coerced keys: 8 ms
Plain JS array using find(): 127 ms
As I'm using React Native I always have to look out for the 16 ms limit if I want to achieve 60 fps. The benchmark values does not seem to be linear. Running the test with only 100 lookups takes 1 ms with Map and 2 ms with List. That's quite expensive.
Have I done something wrong in the benchmarks? Otherwise I guess I'll have to leave Immutable.js out of this for now :(
Test code
let Immutable = require('immutable');
let mapTest = Immutable.Map()
.set(1, Immutable.Map({value: 'one'}))
.set(2, Immutable.Map({value: 'two'}))
.set(3, Immutable.Map({value: 'three'}))
.set(4, Immutable.Map({value: 'four'}))
.set(5, Immutable.Map({value: 'five'}))
.set(6, Immutable.Map({value: 'six'}))
.set(7, Immutable.Map({value: 'seven'}))
.set(8, Immutable.Map({value: 'eight'}))
.set(9, Immutable.Map({value: 'nine'}))
.set(10, Immutable.Map({value: 'ten'}));
let listTest = Immutable.fromJS([
{key: 1, value: 'one'},
{key: 2, value: 'two'},
{key: 3, value: 'three'},
{key: 4, value: 'four'},
{key: 5, value: 'five'},
{key: 6, value: 'six'},
{key: 7, value: 'seven'},
{key: 8, value: 'eight'},
{key: 9, value: 'nine'},
{key: 10, value: 'ten'}
])
let objTest = {
1: {value: 'one'},
2: {value: 'two'},
3: {value: 'three'},
4: {value: 'four'},
5: {value: 'five'},
6: {value: 'six'},
7: {value: 'seven'},
8: {value: 'eight'},
9: {value: 'nine'},
10: {value: 'ten'}
};
let arrayTest = [
{key: 1, value: 'one'},
{key: 2, value: 'two'},
{key: 3, value: 'three'},
{key: 4, value: 'four'},
{key: 5, value: 'five'},
{key: 6, value: 'six'},
{key: 7, value: 'seven'},
{key: 8, value: 'eight'},
{key: 9, value: 'nine'},
{key: 10, value: 'ten'}
];
const runs = 1e6;
let i;
let key;
let hrStart;
console.log(' ')
console.log('mapTest -----------------------------')
key = 1;
hrstart = process.hrtime();
for(i=0; i<runs; i++) {
let result = mapTest.getIn([key, 'value'] )
key = (key >= 10) ? 1 : key + 1;
}
hrend = process.hrtime(hrstart);
console.info("Execution time (hr): %dms", hrend[0] * 1000 + hrend[1]/1000000);
console.log(' ')
console.log('listTest -----------------------------')
key = 1;
hrstart = process.hrtime();
for(i=0; i<runs; i++) {
let result = listTest
.find(item => item.get('key') === key)
.get('value');
key = (key >= 10) ? 1 : key + 1;
}
hrend = process.hrtime(hrstart);
console.info("Execution time (hr): %dms", hrend[0] * 1000 + hrend[1]/1000000);
console.log(' ')
console.log('arrayTest -----------------------------')
key = 1;
hrstart = process.hrtime();
for(i=0; i<runs; i++) {
let result = arrayTest
.find(item => item.key === key)
.value
key = (key >= 10) ? 1 : key + 1;
}
hrend = process.hrtime(hrstart);
console.info("Execution time (hr): %dms", hrend[0] * 1000 + hrend[1]/1000000);
console.log(' ')
console.log('objTest -----------------------------')
key = 1;
hrstart = process.hrtime();
for(i=0; i<runs; i++) {
let result = objTest[key].value
key = (key >= 10) ? 1 : key + 1;
}
hrend = process.hrtime(hrstart);
console.info("Execution time (hr): %dms", hrend[0] * 1000 + hrend[1]/1000000);
来源:https://stackoverflow.com/questions/41999419/immutable-js-map-with-numeric-key-performance-test-included