In a state-managing javascript framework (eg: React), if you have a collection of objects to store in state, which is the more useful and/or performant dataset type to hold them all, an object or an array? Here are a few of the differences I can think of that might come up in using them in state:
Referencing entries:
With objects you can reference an entry directly by its key, whereas with an array you would have to use a function like dataset.find(). The performance difference might be negligible when doing a single lookup on a small dataset, but I imagine it gets larger if the find function has to pore over a large set, or if you need to reference many entries at once.
Updating dataset:
With objects you can add new entries with {...dataset, [newId]: newEntry}, edit old entries with {...dataset, [id]: alteredEntry} and even edit multiple entries in one swoop with {...dataset, [id1]: alteredEntry1, [id2]: alteredEntry2}. Whereas with arrays, adding is easy [...dataset, newEntry1, newEntry2], but to edit you have to use find(), and then probably write a few lines of code cloning the dataset and/or the entry for immutability's sake. And then for editing multiple entries it's going to either require a loop of find() functions (which sounds bad for large lists) or use filter() and then have to deal with adding them back into the dataset afterwards.
Deleting
To delete a single entry from the object dataset you would do delete dataset[id] and for multiple entries you would either use a loop, or a lodash function like _.omit(). To remove entries from an array (and keep it dense) you'd have to either use findIndex() and then .slice(index, 1), or just use filter() which would work nicely for single or multiple deletes. I'm not sure about the performance implications of any of these options.
Looping/Rendering: For an array you can use dataset.map() or even easily render a specialized set on the fly with dataset.filter() or dataset.sort(). For the object to render in React you would have to use Object.values(dataset) before running one of the other iteration functions on it, which I suppose might create a performance hit depending on dataset size.
Are there any points I'm missing here? Does the usability of either one depend perhaps on how large the dataset is, or possibly how frequent the need to use "look up" operations are? Just trying to pin down what circumstances might dictate the superiority of one or the other.
There's no one real answer, the only valid answer is It dependsTM.
Though there are different use-cases that requires different solutions. It all boils down to how the data is going to be used.
A single array of objects
Best used when the order matters and when it's likely rendered as a whole list, where each item is passed from the list looping directly and where items are rarely accessed individually.
This is the quickest (least developer-time consuming) way of storing received data, if the data is already using this structure to begin with, which is often the case.
Pros of array state
Items order can be tracked easily,
Easy looping, where the individual items are passed down from the list.
It's often the original structure returned from API endpoints,
Cons of an array state
Updating an item would trigger a render of the full list.
Needs a little more code to find/edit individual items.
A single object by id
Best used when the order doesn't matter, and it's mostly used to render individual items, like on an edit item page. It's a step in the direction of a normalized state, explained in the next section.
Pros of an object state
Quick and easy to access/update by id
Cons of an object state
Can't re-order items easily
Looping requires an extra step (e.g. Object.keys().map)
Updating an item would trigger a render of the full list,
Likely needs to be parsed into the target state object structure
Normalized state
Implemented using both an object of all items by id, and an array of all the id strings.
{
items: {
byId: { /**/ },
allIds: ['abc123', 'zxy456', /* etc. */],
}
}
This becomes necessary when:
all use-cases are equally likely,
performance is a concern (e.g. huge list),
The data is nested a lot and/or duplicated at different levels,
re-rendering the list as undesirable side-effects
An example of an undesirable side-effect: updating an item, which triggers a full list re-render, could lose a modal open/close state.
Pros
Items order can be tracked,
Referencing individual items is quick,
Updating an item:
Requires minimal code
Doesn't trigger a full list render since the full list loops over allIds strings,
Changing the order is quick and clear, with minimal rendering,
Adding an item is simple but requires adding it in both dataset
Avoids duplicated objects in nested data structures
Cons
Individual removal is the worse case scenario, while not a huge deal either.
A little more code needed to manage the state overall.
Might be confusing to keep both state dataset in sync.
This approach is a common normalization process used in a lot of places, here's additional references:
Redux's state normalization is a strongly recommended best practice,
The normalizr lib.
I'm dealing with a hand full of javascript objects that i get from an external api-library. I want to store the incoming objects in my react application using redux.
These objects are es2015 classes that also come with two handy methods called fromJSON and toJSON. As i want my redux store to be serializable (as it should be) i need a way to translate them to plain objects (toJSON does that by giving me back a dict). In my application i need to use these Objects as they come from the API since i need the methods attached to them and the api-client also wants these specific objects.
Is this a common need as i can't find much about this online or am i totally going the wrong path? How would i implement such a transformation? I'm currently thinking about attaching the es2015 classes to my action and call toJSON in my reducers. I could then create specific selectors that catch the json from redux and convert them back to the classes using the fromJSON functionality (would i have to memoize them?). These selectors could then be used in mapStateToProps to map then finally to a prop.
Let me know what you think about this and how/if i could improve this process.
Redux says the state SHOULD be serializable. It should be so, because at some point you might need to store the state locally (via some form of localstorage). But this does not mean the state has to consist of plain objects.
For example, lot of projects use Immutable.js objects for their state. There is an overhead serializing Immutable.js objects. But it can be done using transit-immutable-js.
From what I understand this principal is similar to your question.
What I understand from your question is that (whatever you mean by ES2015 classes) are deserializable/serilizable by fromJSON and toJSON methods, which is all you need.
Thus you can use your "API" objects in state and serialize it only when you need to store it locally.
I have a situation in which I would greatly benefit from the use of function caching (memoization).
However, my function takes a single argument, whose value is a very large and multi-dimensional Array.
The standard way to do caching ( and the only way that I can think of) is to create a cache property on the function itself (cache is a hash). Each run of the function, you can check for the existence of myFunc.cache[arg] and simply return that value if it exists, otherwise you perform the calculations as normal and add the argument as a property of cache afterwards.
However, it appears that javascript does not try to evaluate the strings used as hash keys when you are creating them and always just treats them as strings. example
I could apply JSON.stringify to the argument, but because the array will large and nested, I am wondering if there is a more efficient way to identify unique arguments.
This question is basically what I was asking without knowing it:
JavaScript Hashmap Equivalent
In my case, the simplest solution is just to manually make the 0th index of my arrays an ID that I can use as the hash key.
Without a means of doing this, or something similar, you have to either create your own hashmap (outlined in the linked question) or wait for the official Map object to be implemented.
I want a reusable list with my own custom behaviors (e.g. updateOrAppendByItemId) to hold a retrieved array for enumeration via ngRepeat. The main purpose is to hold live collections - contents might be changed locally or remotely (via SignalR). It should:
enumerate properly in an ng-repeat directive
not render my custom behaviors as items (rows)
retain sort order from de-serialized source
behave across modern browsers
avoid unnecessary list copying
How should it generally be constructed?
I've immediately run into conceptual problems when building it a few different ways. A JavaScript Array can't be subclassed. Copying my behavioral prototype directly to an Array instance results in undesired enumerable properties. Storing contents in object properties loses source item order.
Each of those problems currently has only theoretical severity to me, with real-world consequences that depend on both undocumented AngularJS behavior and browser differences. I'm hoping someone who has touched this problem before can save me lots of trial-and-error.
Research:
Since I'm starting that potentially lengthy investigation I was hoping to avoid, I'll post my work here.
Angular's ngRepeat first tests if a collection isArrayLike(). If it is, it simply iterates through the length of the collection numerically. That seems highly intentional (although I wouldn't want to depend on the interpretation of the internal function isArrayLike()).
In the one browser I tested in so far, neither of the following change the effective array contents.
// safe
array.myMethod = true;
array['myMethod'] = true;
However, the following is disastrous: /grin
array['5'] = true;
So, that's ECMA spec:
A property name P (in the form of a String value) is an array index if
and only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not
equal to 232−1.
I may not have to go any further. I'll try this solution and report back.
I do not have time to create a fiddle right now, but will definetely do so tomorrow.
Basically my problem is caching a data model retrieved from a restful get endpoint and comparing to a new model returned by a restful updated endpoint in order to be able to highlight the changed values in the UI.
The way I handled this is by using underscore's each() and angular.compare() methods in order to loop through a collection and compare it object key by object key.
However this feels wrong and I have problems in getting the updated key name.
Is there a better, accepted way to do this as I cannot find anything anywhere, just a bunch of people generally asking the same question and getting answers like: 'Use a watcher and underscore/angular methods, it is easy'.
For what I have understand, what you are trying to do is the correct way to see it.
You have to compare object by object.
To help you between the old collection and the new one, a watcher is not a bad answer. In fact the angular watcher can give you the old collection and the new one as parameters. So all you got to do is make a check object by object. And had a special treamenton the different values.
Hope this can help you.