I try to create a "keyed" array, where items can be accessed by the index (like a regular array) and a .key
property defined in the item object.
The problem(s):
- I have concern about the consistency
- Array methods can be overridden
- Implement all methods (
.slice
,.splice
. etc.)
class Commands extends Array {
constructor(items) {
super(...items);
items.forEach(item => {
this[item.key] = item;
});
}
push(...items) {
super.push(...items);
items.forEach(item => {
this[item.key] = item;
});
}
}
const commands = new Commands([
{
key: "a",
name: "A",
},
{
key: "b",
name: "B",
},
{
key: "c",
name: "C",
},
]);
console.log("commands", commands);
console.log("item a", commands.a);
console.log("item c", commands[2]);
commands.a.name = "foo";
//commands.slice(0, 1); // throws error "TypeError: Spread syntax requires ...iterable[Symbol.iterator] to be a function"
console.log(commands);
So my question is: Is this a good approach (in terms of "for most use cases"), or should handle it in a other way, e.g. wrap the array in a proxy and handle the "get by key" stuff in get trap, or something complete else?
EDIT:
Answer to @pilchard question in the comments: I want to have a "shortcut", something that is user defined, to access the item. In a large array to work with indexes is not as easy as to say you want item commands.c
compared to:
const index = commands.indexOf((item) => {
return item.key === "c";
});
const element = commands[index];
3
Answers
Your approach to creating a "keyed" array by extending the Array class and adding properties to access items by their keys is interesting and has some merits. However, there are also potential issues and alternative solutions to consider.
Direct Access by Key: You can access elements directly by their key, which is convenient.
Array-like Behavior: The class still retains standard array behavior, allowing for methods like push, slice, etc.
Consistency: The approach may lead to inconsistencies, especially if items are modified or removed. For instance, if an item is removed, its key property will still be accessible unless explicitly deleted.
Array Methods: Overriding all array methods to maintain the key-indexed behavior can be cumbersome and error-prone. Methods like slice, splice, and others would need to be carefully handled to update the keyed properties correctly.
Performance: The approach might not be as performant for very large arrays, as updating the keyed properties involves iterating over the items.
Alternative Approach: Using a Proxy
A Proxy can provide a cleaner and more flexible way to handle the "get by key" behavior. Here’s an example of how you can achieve this:
Explanation
Proxy Handler: The Proxy handler intercepts the get operation. If the property exists as an index, it returns the corresponding item. Otherwise, it searches for the item by its key.
Simplified Management: You don’t need to override array methods. The Proxy takes care of returning items based on either index or key.
Flexibility: This approach maintains the flexibility and consistency of accessing elements either by index or key without modifying the original array methods.
To conclude
Using a Proxy provides a more robust solution for your use case. It simplifies managing the dual access pattern (by index and key) and avoids the pitfalls of overriding array methods. For most use cases, this method will likely be more maintainable and less error-prone.
You can use a Proxy to retrieve values from an
Array
two ways. Here’s an example:You can use
Proxy
, to provide access with both the keys and indices. You can also cache the found items by key, reset the cache on any methods that mutate array