Hi !
Given the following sample items :
ID | First name | Age |
---|---|---|
xvZwiCpi |
Naomi | 42 |
Nzd9UsGT |
Naomi | 24 |
QiDXP2wA |
Thea | 53 |
JpYeAY7H |
Jeremy | 35 |
I can store these in an array :
const data = [
{ id: 'xvZwiCpi', firstName: 'Frederic', age: 42 },
{ id: 'Nzd9UsGT', firstName: 'Naomi', age: 24 },
{ id: 'QiDXP2wA', firstName: 'Thea', age: 53 },
{ id: 'JpYeAY7H', firstName: 'Mathew', age: 35 }
];
Thus access them the same way by ID :
console.log(data.find(item => item.id === 'xvZwiCpi'));
And by properties :
console.log(data.find(item => item.firstName === 'Frederic').id);
Or I can store these in an object :
const data = {
'xvZwiCpi': { firstName: 'Frederic', age: 42 },
'Nzd9UsGT': { firstName: 'Naomi', age: 24 },
'QiDXP2wA': { firstName: 'Thea', age: 53 },
'JpYeAY7H': { firstName: 'Mathew', age: 35 }
};
Thus more easily access properties by ID :
console.log(data['xvZwiCpi'].firstName);
But more hardly access ID by properties :
console.log(Object.entries(data).find(([id, item]) => item.firstName = 'Frederic')[0]);
I could duplicate IDs :
const data = {
'xvZwiCpi': { id: 'xvZwiCpi', firstName: 'Frederic', age: 42 },
'Nzd9UsGT': { id: 'Nzd9UsGT', firstName: 'Naomi', age: 24 },
'QiDXP2wA': { id: 'QiDXP2wA', firstName: 'Thea', age: 53 },
'JpYeAY7H': { id: 'JpYeAY7H', firstName: 'Mathew', age: 35 }
};
To slightly simplify that previous line :
console.log(Object.values(data).find(item => item.firstName = 'Frederic').id);
But what if a single variable type could allow doing both operations easily ?
console.log(data['xvZwiCpi'].firstName);
console.log(data.find(item => item.firstName === 'Frederic').id);
Does that exist ?
If not, I’m thinking about implementing it that way :
const data = new Proxy([
{ id: 'xvZwiCpi', firstName: 'Frederic', age: 42 },
{ id: 'Nzd9UsGT', firstName: 'Naomi', age: 24 },
{ id: 'QiDXP2wA', firstName: 'Thea', age: 53 },
{ id: 'JpYeAY7H', firstName: 'Mathew', age: 35 }
], {
get: (array, property) =>
array[property]
||
array.find(item => item.id === property)
});
In which case I’d put it in a lib, but how would this be named ?
I’d also make a second implementation that would enforce ID uniqueness and use Map
to map IDs with indexes instead of running find
: while the first implementation would be fine for static data, the second one would be more suitable for dynamic data.
Would this make sense ?
Thanks
I’m pretty sure I’ve been in your situation but haven’t created a dictionary/array hybrid.
Without any more details about your use case and situation, I can imagine a few pitfalls with your solution:
- Serialization might not behave as you would expect (JSON.stringify).
- 3rd-party functions might not be able to deal with your data structure properly (again, potentially unexpected behavior).
- You can’t easily access array methods (find, filter, map etc).
- How do you distinguish between ID access and index access?
ArrayLike([ {id: "1" }, { id: "2" } ])[1]
returns{ id: "2" }
, how do you access{ id: "1" }
by ID? - It’s harder to reason about access time of lookups. However, this might not be a concern of yours.
- It may cause confusion if you’re working with other developers.
That being said, unless you work in an environment where your code should be easily understandable by others, the best way to find out if this is a good idea or not, is to try :)
Me personally, I usually use an
associateBy
function, when I need a values-by-ID structure. Of course this is not as convenient to use as your approach, but it’s good enough for me.// this WILL drop elements if key is not unique function associateBy(array, key) { return array.reduce((acc, el) => ({ ...acc, [el[key]]: el }), {}); } associateBy([ {id: "foo"}, {id: "bar"} ], "id").foo; // -> {id: "foo"}
Good luck!
dictionary/array hybrid
That’s a name, thanks !
Serialization might not behave as you would expect (JSON.stringify)
Actually, my implementation idea relying on
Proxy
don’t have this issue.As for other implementations, they do have this issue, but as they say, hybrid array use cases don’t generally involve the need to stringify, and even when they do, using
Object.defineProperty
with{ enumerable: false }
(or replacing thetoJSON
method) would fix it.3rd-party functions might not be able to deal with your data structure properly (again, potentially unexpected behavior). You can’t easily access array methods (find, filter, map etc).
Actually, it would still be an
Array
, so no, there shouldn’t be any problems, and yes, those methods definitely work, which is precisely what said I want to achieve.How do you distinguish between ID access and index access?
If your IDs are integers then there is no need for an hybrid at all, precisely because all you have to do is put each item at the same position as their ID.
It’s harder to reason about access time of lookups. However, this might not be a concern of yours.
I’ll definitely run benchmarks so that users would be aware of performance losses, if any. But use cases of hybrid arrays are mostly small datasets so it usually shouldn’t be a concern indeed.
It may cause confusion if you’re working with other developers.
If I implement this, it will be as a documented and commented package, so it shouldn’t be that much of a problem.
I think I misunderstood your initial post (and definitely didn’t read it as carefully as I should have 😅).
Do I understand your correctly that your goal is a companion object for your arrays that simplifies access? Not a new data structure that you’d user instead of arrays? If so, most of my points are moot.
If your IDs are integers then there is no need for an hybrid at all, precisely because all you have to do is put each item at the same position as their ID.
If you don’t treat IDs as opaque values, this is true.
I’ll definitely run benchmarks so that users would be aware of performance losses, if any. But use cases of hybrid arrays are mostly small datasets so it usually shouldn’t be a concern indeed.
I think my point is actually wrong (it was really late when I was writing my initial response). Performance would be O(n), since that’s the worst case scenario.
Anyways, I hope you could take something useful from my answer.
Happy hacking :D