Names!
JavaScript convention is to use camelCase
for names rather than snake_case
.
Using a Hash Map
Using Array.indexOf to locate unique entries has a complexity of \$O(n)\$ for each record in the source array, where \$n\$ is number of unique items. This can be reduced to \$O(1)\$ by using a hash map.
JavaScript has several ways to use hash maps (Object property names, Map, Set, WeakMap, and WeakSet)
In this case you can use Map to group by unique item values Map(key, {date: key})
creates a hash for key
and stores the object {date: key}
with the hash
Segregate names from data
This type of task may often require minor changes when data sources are outside your control. It pays to use a more generic solution that removes the hard coded property naming from the function.
To remove the hard coded properties date
, name
, value
from the functions body we can divide the task into two,
- Create a record for each unique named property
- Add a new property (key/value pair) to an object based on the value of two properties in a source object.
Grouping by property
Using a Map
create records for each unique named property value and add key value pairs as defined by the function above
const groupBy = (data, name, addKeyVal) => { const res = new Map(); for (const record of data) { const val = record[name]; // unique Value addKeyVal( // Add name and value to unique rec res.get(val) ?? res.set(val, {[name]: val}).get(val), // get unique rec, Create if needed record ); } return [...res.values()]; // return as array of objects }
For more on the operator ?? (Nullish coalescing operator) used.
Adding properties
A generic function that returns a function to extract and add the named key/value pair to a existing object
const keyValToRecord = (key, val) => (dest, src) => dest[src[key]] = src[val];
Names defined outside function
We can now extract the data with the property names being independent of the function's code
const valueByDate = groupBy(sourceData, "date", keyValToRecord("name", "value"));
Say your data source came with more data, eg each house had an insured value. Rather than rewrite/copy the whole function changing each named reference to value
to insured
you need only change the call
const insuredByDate = groupBy(sourceData, "date", keyValToRecord("name", "insured"));
Rewrite
Putting it all together we get
const keyValToRecord = (key, val) => (dest, src) => dest[src[key]] = src[val]; const groupBy = (data, name, addKeyVal) => { const res = new Map(); for (const record of data) { const val = record[name]; addKeyVal(res.get(val) ?? res.set(val, {[name]: val}).get(val), record); } return [...res.values()]; } // Given source data const sourceData = [{date: 1, name: "House1", value: 5}, {date: 1, name: "House2", value: 6}, {date: 1, name: "House3", value: 7}, {date: 1, name: "House4", value: 8}, {date: 2, name: "House1", value: 2}, {date: 2, name: "House2", value: 1}, {date: 2, name: "House3", value: 3}, {date: 2, name: "House4", value: 1}]; // Extract the data grouped by date console.log(groupBy(sourceData, "date", keyValToRecord("name", "value")));
true
,false
andnull
.\$\endgroup\$