derived
v0.3.3
Published
Instant views and derived indexes for Javascript objects.
Downloads
29
Maintainers
Readme
Objects with derived indexes!
What does it do?
It creates automatic, synchronous and always-updated "views" for your data. You instantiate a main data object and define functions for creating derived indexes on it. Later you can easily access the data on the derived indexes.
It reduces bloat, verbose value getters and automatizes data organization in the context of your app. Perhaps others would call this "a database". This name would be misleading, but the power of automatic synchronous subindexes cannot be overstated.
Example
(view and fiddle with this example on Tonic)
const assert = require('chai').assert
const D = require('derived')
/* instantiate your data fundamentals -- or just begin from zero with `new D()` */
const data = new D({
'#1': {
name: 'shangri-la',
zipcode: 27498,
inhabitants: [{
name: 'catherine',
birthday: '11-17'
}]
},
'#2': {
name: 'el dorado',
zipcode: 69712,
inhabitants: [{
name: 'pizarro',
birthday: '04-01'
}]
},
'#3': {
name: 'atlantis',
zipcode: 18315,
inhabitants: [{
name: 'plato',
birthday: '07-18'
}]
},
'#4': {
name: 'avalon',
zipcode: 37851
}
})
/* reindex the data on the go, for much faster and easy access later */
data.derived('cityId', (k, v) => [v.name, k])
/* now you can access it easily, no need for costly searches on independent index management */
assert.equal(data.cityId['shangri-la'], '#1')
/* add data using super useful methods (you can't use normal methods, it's a Javascript limitation) */
let cityId = data.cityId
data.push([cityId.avalon, 'inhabitants'], {name: 'arthur', birthday: '12-30'})
data.push([cityId.avalon, 'inhabitants'], {name: 'morgana', birthday: '12-31'})
/* create multiple derived indexes */
data.derived('personZip', function (cityId, cityData) {
if (cityData.inhabitants) {
cityData.inhabitants.forEach(person => {
this.emit(person.name, cityData.zipcode)
})
}
})
/* no more `var personZip = zipcodesIndex[personsIndex[personId]]` or other verbose tricks
access your data directly */
let personzip = data.personZip
assert.equal(personzip['catherine'], 27498)
assert.equal(personzip['arthur'], 37851)
assert.equal(personzip['morgana'], 37851)
/* indexes can be of anything (and they don't need names): */
let birthdaysByMonth = data.derived(function (_, city) {
if (city.inhabitants) {
city.inhabitants.forEach(person => {
let month = parseInt(person.birthday.split('-')[0], 10)
this.emit(month, person.name)
})
}
})
assert.deepEqual(birthdaysByMonth.getAll(12), ['arthur', 'morgana'])
/* everything changes synchronously and automatically whenever you update the main data source */
data.set(`${cityId['atlantis']}.inhabitants.0.birthday`, '01-01')
assert.equal(birthdaysByMonth[1], 'plato')
Other
- The derivation functions are inspired by CouchDB. Not equal, though, but the
emit
thing... well, it doesn't matter. - These getters and setters only work on the main data source, the data in derived indexes is read-only and can be accessed through normal Javascript syntax.
- Other features are planned (please say what would you find more useful):
- immutable main data source
- immutable derived indexes
- queryable derived indexes, returning arrays of values sorted by emitted keys
- derived indexes based on the entire data object, instead of in each of its keys
- derived indexes on sublevels