immutable-js: .fromJS doesn't preserve key types
I would expect .fromJS
to preserve key types, at lease for integer. Here is my test case :
var obj = {42: "test"};
var test = Immutable.fromJS(obj);
obj.get(42); // undefined
obj.get("42"); // "test"
I might be missing something but I find it odd having to .toString()
every time I .get
a value.
About this issue
- Original URL
- State: closed
- Created 9 years ago
- Reactions: 5
- Comments: 31 (3 by maintainers)
Sorry to open this thing up again, but I’m working in a very large codebase where a migration from Integers -> Strings could potentially be very painful. Is there a way to extend a Map to a StringMap? Something like:
Any advice on how to approach that? Edge cases I should be aware of?
I kinda realized after making this I should just make the
Map
aList
, but here is a fromJS fn that will recursively make all the number like keys of an object integers.This is actually just JavaScript doing it’s thing. JavaScript Object keys are always strings.
@Altiano It’s not a quirk of immutable.js, it’s a “quirk” of JavaScript.
Object keys are property names. Property names are always strings (or symbols, but that’s probably not as important). If you use non-string property names when defining properties, the names will be converted to strings. Don’t take this as a snark but I strongly recommend you solidify your understanding of the language before moving on.
I strongly recommend the You Don’t Know JS series of books by getify because they’re available online for free and are pretty exhaustive without dwelling too much on the author’s personal opinions. If you can’t set aside the time to read them from back to back (which is understandable) just use them as a “take what you need” reference and look up the parts of JS you find confusing.
FWIW if you want to observe the behaviour more directly, open your browser’s console (F12 or Ctrl+Shift+J/Cmd+Shift+J depending on your platform) and compare the following (assuming Chrome/Edge or a recent version of Firefox):
The last two lines show you what native maps and objects look like when converted to arrays of key-value pairs. While as you can see it is possible to treat objects like maps, the keys are always strings. There are also some other caveats that make objects unfit as substitutes for proper maps (whether native or immutable.js).
(Another limitation is that some object property names are “special” and may have unintended side-effects, so it’s a bad idea to use objects as maps when dealing with potentially malicious or uncontrolled values for keys.)
@Altiano read the original discussion again:
when creating maps from objects, keys are always converted to strings because object property names are already strings
when creating maps from arrays of key-value pairs, the keys’ types are preserved
This is the exact behaviour you’re seeing: the keys aren’t converted by immutable. The original discussion was the result of a confusion about JavaScript’s behaviour (i.e. object keys always being strings), not something immutable does.
Thank you for a great discussion. I learned a lot. I see that the issue is closed, but I’d like to add some points for others to find.
As @leebyron says, consistency is good, i.e. use string-id OR integer-id. If you like me use Immutable with Redux and your data originally comes from a database, you might have integers as both primary and foreign keys. If you need to do lookups in your Redux store by foreign key you’ll be best off to use integers as keys in your Immutable.Map. This way you never need to “manually coerce” to string when performing lookups.
Here is how I load data into Redux from my API in order to preserve the keys as integers. Part of the reducer:
N.b. I have an “items” Map for storing the actual data in each slice in the store, hence “items” in the code above. It took me some reading and testing before landing in this key-solution. It really works well and I recommend it.