map-filter-reduce
Advanced tools
Comparing version 2.0.0 to 2.1.0
{ | ||
"name": "map-filter-reduce", | ||
"description": "", | ||
"version": "2.0.0", | ||
"version": "2.1.0", | ||
"homepage": "https://github.com/dominictarr/map-filter-reduce", | ||
@@ -6,0 +6,0 @@ "repository": { |
@@ -210,3 +210,3 @@ # map-filter-reduce | ||
In the following query, | ||
In the following query, | ||
``` js | ||
@@ -221,9 +221,10 @@ {$reduce: { | ||
group can be applied to more than one field by using an array. | ||
Sometimes, it's easier to work with an array or stream of the groups, | ||
but sometimes we'd rather have elements grouped into an object. | ||
For that we use the `$group` operator. | ||
``` js | ||
{$reduce: {country: 'country', city: 'city', population: {$count: true}}} | ||
{$reduce: {$group: ['country', 'city'], $reduce: {$count: true}}} | ||
``` | ||
This would return a sequence of {country, city, population} tripples. | ||
This would return a object, with the shape `{<country>:{<city>: <population>}}`. | ||
@@ -242,1 +243,4 @@ Note that, like in `$map` arrays can be used to drill deep into | ||
@@ -9,5 +9,7 @@ var u = require('./util') | ||
function isSimple(query) { | ||
for(var k in simple) if(u.has(query, k)) return k | ||
if(query.$reduce) return amake(query.$reduce) | ||
for(var k in simple) if(u.has(query, k)) return lookup(simple[k], query[k]) | ||
} | ||
//this should be a reduce and a map | ||
function lookup(reduce, path) { | ||
@@ -28,3 +30,3 @@ if(path === true) return reduce | ||
function group (g, reduce) { | ||
function arrayGroup (g, reduce) { | ||
@@ -51,11 +53,25 @@ function compare (a, b) { | ||
function objectGroup (g, reduce) { | ||
if('string' === typeof g) g = [g] | ||
return function (a, b) { | ||
var A = a = a || {} | ||
u.each(g, function (k, i) { | ||
var last = (i == (g.length - 1)) | ||
var v = u.get(b, k) | ||
A[v] = last ? reduce(A[v], b) : A[v] || {} | ||
A = A[v] | ||
}) | ||
return a | ||
} | ||
} | ||
function make (query) { | ||
var k = isSimple(query) | ||
if(k) return lookup(simple[k], query[k]) | ||
else if(u.isObject(query)) | ||
return multi(map(query, function (q, k) { | ||
if(k == '$group') return undefined | ||
return make(query[k]) | ||
})) | ||
var r = isSimple(query) | ||
if(r) return r | ||
else if(query.$group) | ||
return objectGroup(query.$group, gmake(query.$reduce)) | ||
else if(u.isObject(query)) { | ||
return multi(map(query, gmake)) | ||
} | ||
else return function (a, b) { | ||
@@ -66,8 +82,12 @@ return b[query] | ||
function gmake (query) { | ||
if(isSimple(query)) return make(query) | ||
function amake (query) { | ||
if(query.$group) return objectGroup(query.$group, make(query.$reduce)) | ||
var r = isSimple(query) | ||
if(r) return r | ||
var paths = [] | ||
u.each(query, function traverse (value) { | ||
if(isSimple(value)) return | ||
if(isSimple(value) || value.$group || value.$reduce) return | ||
else if(u.isObject(value)) each(value, traverse) | ||
@@ -77,6 +97,11 @@ else if(value) paths.push(value) | ||
return paths.length ? group(paths, make(query)) : make(query) | ||
return paths.length ? arrayGroup(paths, make(query)) : make(query) | ||
} | ||
module.exports = gmake | ||
function gmake (query) { | ||
if(query.$group && !query.$reduce) throw new Error('expected $reduce') | ||
return query.$group ? objectGroup(query.$group, gmake(query.$reduce)) : make(query) | ||
} | ||
module.exports = amake | ||
@@ -68,3 +68,3 @@ var tape = require('tape') | ||
objs.reduce(R({ | ||
$group: 'baz', foo: {$max:'foo'}, bar: {$sum: 'bar'} | ||
$group: 'baz', $reduce: {foo: {$max:'foo'}, bar: {$sum: 'bar'}} | ||
}), null), | ||
@@ -76,3 +76,3 @@ {"true": {foo: 10, bar: 10}, "false": {foo: 0, bar: 5}} | ||
objs.reduce(R({ | ||
$group: 'baz', foo: {$max:'foo'}, bar: {$collect: 'bar'} | ||
$group: 'baz', $reduce: {foo: {$max:'foo'}, bar: {$collect: 'bar'}} | ||
}), null), | ||
@@ -124,1 +124,79 @@ {"true": {foo: 10, bar: [2,3,5]}, "false": {foo: 0, bar: [1,4]}} | ||
tape('more groups, object', function (t) { | ||
t.deepEqual(groups.reduce(R({ | ||
$group: ['country', 'dwelling'], | ||
$reduce: {$collect: 'name'} | ||
}), null), | ||
{ | ||
US: { | ||
apartment: ['pfraze', 'du5t'], | ||
house: ['substack'] | ||
}, | ||
NZ: { | ||
house: ['mix'], | ||
sailboat: ['dominic'] | ||
} | ||
} | ||
) | ||
t.end() | ||
}) | ||
tape('nested object groups', function (t) { | ||
t.deepEqual( | ||
groups.reduce(R({ | ||
$group: 'country', | ||
$reduce: { | ||
population: {$count: true}, | ||
housing: {$group: 'dwelling', $reduce: { $count: true }} | ||
} | ||
}), null), | ||
{ US: { population: 3, housing: { apartment: 2, house: 1 } }, | ||
NZ: { population: 2, housing: { house: 1, sailboat: 1 } } } | ||
) | ||
t.end() | ||
}) | ||
tape('nested array groups', function (t) { | ||
t.deepEqual( | ||
groups.reduce(R({ | ||
dwelling: 'dwelling', | ||
citizens: {$reduce: { | ||
name: 'name', country: 'country' | ||
}} | ||
}), null), | ||
[ | ||
{dwelling: 'apartment', citizens: [ | ||
{name: 'du5t', country: 'US'}, | ||
{name: 'pfraze', country: 'US'} | ||
]}, | ||
{dwelling: 'house', citizens: [ | ||
{name: 'mix', country: 'NZ'}, | ||
{name: 'substack', country: 'US'} | ||
]}, | ||
{dwelling: 'sailboat', citizens: [ | ||
{name: 'dominic', country: 'NZ'} | ||
]} | ||
] | ||
) | ||
t.end() | ||
}) | ||
tape('nested array groups', function (t) { | ||
t.deepEqual( | ||
groups.reduce(R({ | ||
dwelling: 'dwelling', | ||
citizens: {$group: 'country', $reduce: { | ||
$count: true | ||
}} | ||
}), null), | ||
[ | ||
{dwelling: 'apartment', citizens: {US: 2}}, | ||
{dwelling: 'house', citizens: {NZ: 1, US: 1}}, | ||
{dwelling: 'sailboat', citizens: {NZ: 1}} | ||
] | ||
) | ||
t.end() | ||
}) | ||
@@ -89,3 +89,6 @@ 'use strict' | ||
if(Array.isArray(obj)) return obj.forEach(iter) | ||
for(var k in obj) iter(obj[k], k, obj) | ||
else if(isObject(obj)) | ||
for(var k in obj) iter(obj[k], k, obj) | ||
else | ||
iter(obj) | ||
} | ||
@@ -92,0 +95,0 @@ |
27280
811
244