0

I have an array of items which I sometimes need to iterate and sometimes access a member directly. So I decided to keep references in two variables, one array and one object. I do something like this:

const itemsArr = [];
const itemsObj = {};
const addItem = (data) => {
    const item = {
        id: data.id,
        name: data.name
    };
    itemsArr.push(item);
    itemsObj[data.id] = item;
}

const removeItem = (data) => {
    let i;
    for (i = 0; i < itemsArr.length; i++) {
        if (itemsArr[i].id === data.id) {
            itemsArr.splice(i, 1);
            break;
        }
    }
    itemsObj[data.id] = null;
    delete itemsObj[data.id];
}

const getWithId = (id) => {
    return itemsObj[id];
}

const getWithName = (name) => {
    let i;
    for (i = 0; i < itemsArr.length; i++) {
        if (itemsArr[i].name === name) {
            return itemsArr[i];
        }
    }
    return null
}

So I manage two objects and use one or another depending on the task, I feel like this is the most performant way but maybe there are better ways for this, like a Map or Set.

Is there a single JavaScript data structure that will outperform arrays in iteration and objects in lookup?

I think Object.keys have additional performance costs for iterating an object, similarly Array.filter for lookup on an array, so my intuition is to use arrays for iteration and objects for lookup, but if there is a single data structure that is optimized for both, I would like to know and use only one items in my code.

Thanks

4
  • Just to be clear, you never access by index, right? Just id or name? Commented Jan 26, 2022 at 7:54
  • Not sure what do you mean by index. Like position in array? If so, no. I use object keys as same as the item id's and acces with that. In reality, items have more keys, like dateCreated etc but I just wrote two for simplicity. Commented Jan 26, 2022 at 7:56
  • Yes, the position of an element in array is called its index. Commented Jan 26, 2022 at 7:58
  • Yeah no, the ordering is not relevant for me. I just use an array since I think its the fastest thing to iterate :) Commented Jan 26, 2022 at 7:59

1 Answer 1

1

First, for the lookup-by-id part of things, I'd suggest a Map, not an object. Objects are optimized for the common programming use case where their set of properties is fairly consistent over time (the values may change, but the shape of the object [what properties it has] mostly doesn't). Map is optimized as a general name/value store that handles that use case better. More in on this MDN page.

Is there a single Javascript data structure that will outperform arrays in iteration and objects in lookup?

No, but the difference in iteration speed between a Map and an array won't matter in the vast majority of applications. Yes, an array will be faster in most cases (it depends a bit on how the array is built), but it's just really unlikely to matter.

But if you only ever access by id or name, you aren't iterating anyway. You might want two maps rather than a map and an array, since that way finding items either way is sublinear rather than linear. It also makes for simpler code:

const itemsByName = new Map();
const itemsById = new Map();
const addItem = (data) => {
    const item = {
        id: data.id,
        name: data.name
    };
    itemsByName.set(data.name, data);
    itemsByid.set(data.id, data);
};

const removeItem = (data) => {
    itemsByName.delete(data.name);
    itemsByid.delete(data.id);
};

const getWithId = (id) => {
    return itemsById.get(id);
};

const getWithName = (name) => {
    return itemsByName.get(name);
};

FWIW, if you didn't have itemsByName in the above because you didn't need name lookup to be sublinear, here's what the code for getWithName would look like using itemsById:

const getWithName = (name) => {
    for (const item of itemsById.values()) {
        if (data.name === name) {
            return data;
        }
    }
    return null;
};
Sign up to request clarification or add additional context in comments.

5 Comments

That's a very good idea actually. But if I do lookups by other keys, I will need more Maps I guess. But I still like the idea! I can do a mixture and use Maps for most common lookups and an array for less frequent ones maybe.
@wololoo - I'd just use a Map and iterate it for the less-common lookups. As I said above, the speed of iteration between a Map and an array just won't matter for the vast majority of cases. Worry about it if/when it's a problem. I've added an example of what getWithName would look like if you didn't have itemsByName and only had itemsById.
Could you please explain why a Map instead of an Object? I could do the same thing with an Object too, right? (Given that the names are unique, which we can safely assume). In my understanding, Map is also an Object so using a plain Object feels better? For this reason I never used Maps before :p
@wololoo - See this secton of the MDN Map documenation. Yes, Maps are objects but get and set aren't doing property operations, so it's not just some added layer on top (in fact, it's more likely a layer of complexity removed).
Yup.. the documentation says Performs better in scenarios involving frequent additions and removals of key-value pairs. which is all I wanted to hear :p Ability to get the number of items and not needing to check hasOwnProperty are good bonuses as well. I think I will try Maps. Thank you so much for your help!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.