I've typically done this by having an array and an object that both reference the same object. E.g.:
var thingies = [];
var thingiesById = Object.create(null);
when adding a "thingy":
thingies.push(thingy);
thingiesById[thingy.id] = thingy;
Example:
var thingies = [];
var thingiesById = Object.create(null);
function addThingy(thingy) {
thingies.push(thingy);
thingiesById[thingy.id] = thingy;
}
// Note intentionally not adding them in ID order
addThingy({id:3, name: "Thingy 3"});
addThingy({id:1, name: "Thingy 1"});
addThingy({id:2, name: "Thingy 2"});
thingies.forEach(function(thingy) {
console.log(thingy.id + ": " + thingy.name);
});
ES2015+'s Maps maintain insertion order and provide iteration semantics that follow that order. You'll want to test that lookup speed on get is as good as you need it to be.
Example:
const thingies = new Map();
function addThingy(thingy) {
thingies.set(thingy.id, thingy);
}
// Note intentionally not adding them in ID order
addThingy({id:3, name: "Thingy 3"});
addThingy({id:1, name: "Thingy 1"});
addThingy({id:2, name: "Thingy 2"});
for (const thingy of thingies.values()) {
console.log(thingy.id + ": " + thingy.name);
}