Skip to content

Commit

Permalink
Make merge and mergeOrSingle return objects by default
Browse files Browse the repository at this point in the history
Closes #5
  • Loading branch information
B3rn475 committed Mar 29, 2018
1 parent f683514 commit fe61d60
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 70 deletions.
93 changes: 51 additions & 42 deletions lib/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,55 +54,56 @@ function concat_accumulate(array, value) { array.push(value); return array; }
var _concat = reduce(concat_accumulate, []);
function concat() { return _concat; }

var _mergeOrSingle = reduce(function _reduce(accumulator, value) {
accumulator.accumulated += 1;
if (accumulator.accumulated === 1) {
var _mergeOrSingle = reduce(function _reduce(status, value) {
status.n_accumulated += 1;
if (status.n_accumulated === 1) {
if (_isPlainObjectOrArray(value)) {
accumulator.value = {};
accumulator.isArray = _.isArray(value);
status.accumulated = {};
status.isArray = status.isArray && _.isArray(value);
} else {
accumulator.value = value;
accumulator.isSimpleValue = true;
return accumulator;
status.accumulated = value;
status.isSimpleValue = true;
return status;
}
} else {
if (accumulator.accumulated === 2 && accumulator.isSimpleValue) {
if (status.n_accumulated === 2 && status.isSimpleValue) {
throw new Exception('fields with a merge policy should always be objects or arrays');
}
if (!_isPlainObjectOrArray(value)) {
throw new Exception('fields with a merge policy should always be objects or arrays');
}
}
accumulator.isArray = accumulator.isArray && _.isArray(value);
status.isArray = status.isArray && _.isArray(value);
_.forEach(value, function (value, key) {
if (accumulator.value.hasOwnProperty(key)) {
accumulator.value[key] = _reduce(accumulator.value[key], value);
if (status.accumulated.hasOwnProperty(key)) {
status.accumulated[key] = _reduce(status.accumulated[key], value);
} else {
accumulator.value[key] = _reduce({accumulated: 0}, value);
status.accumulated[key] = _reduce({n_accumulated: 0, isArray: true}, value);
}
});
return accumulator;
}, {accumulated: 0}, function _terminate(accumulated) {
if (accumulated.isSimpleValue) {
return accumulated.value;
return status;
}, {n_accumulated: 0, isArray: true}, function _terminate(status) {
if (status.isSimpleValue) {
return status.accumulated;
}
if (accumulated.isArray) {
return _.map(accumulated.value, _terminate);
if (status.isArray && status.n_accumulated) {
return _.map(status.accumulated, _terminate);
}
return _.mapValues(accumulated.value, _terminate);
return _.mapValues(status.accumulated, _terminate);
});

function mergeOrSingle() {
return _mergeOrSingle;
}

function createStatus(specials) {
function createStatus(specials, mayBeArray) {
if (typeof specials !== 'object') {
throw new Exception('invalid keys merge configuration');
}
var status = {
n_accumulated: 0,
accumulated: {},
isArray: true
isArray: mayBeArray
};
_.forEach(specials, function (reduce, key) {
if (reduce.hasOwnProperty('accumulator')) {
Expand All @@ -129,14 +130,18 @@ function _mergeCheckObject(status, object) {

function merge(policy, specials) {
if (arguments.length < 1) { policy = mergeOrSingle(); }
var check;
var check,
status;
if (arguments.length < 2) {
specials = {};
status = createStatus(specials, true);
check = _mergeCheckObjectOrArray;
} else {
status = createStatus(specials, false);
check = _mergeCheckObject;
}
return reduce(function (status, object) {
status.n_accumulated += 1;
check(status, object);
_.forEach(object, function (value, key) {
var reduce = specials[key] || policy;
Expand All @@ -149,8 +154,8 @@ function merge(policy, specials) {
}
});
return status;
}, createStatus(specials), function (status) {
var result = status.isArray ? [] : {};
}, status, function (status) {
var result = (status.isArray && status.n_accumulated) ? [] : {};
_.forEach(status.accumulated, function (value, key) {
var reduce = specials[key] || policy;
if (reduce.hasOwnProperty('terminate')) {
Expand Down Expand Up @@ -230,24 +235,23 @@ function _flattenReducer(policy, mapper) {
}
return status;
}
var _accumulate,
status = {};
var status = {};
if (policy.hasOwnProperty('accumulator')) {
status.started = true;
status.accumulated = _.cloneDeep(policy.accumulator);
_accumulate = flattenAccumulate;
status.accumulate = flattenAccumulate;
} else {
_accumulate = function (status, value) {
status.accumulate = function (status, value) {
status.started = true;
value = mapper(value);
status.accumulated = _.first(value);
value = _.tail(value);
_accumulate = flattenAccumulate;
status.accumulate = flattenAccumulate;
return flattenAccumulate(status, value);
};
}
function accumulate(status, value) {
return _accumulate(status, value);
return status.accumulate(status, value);
}
return reduce(accumulate, status, _flattenTerminate(policy));
}
Expand Down Expand Up @@ -306,21 +310,26 @@ function lazy(policy) {
if (typeof policy !== 'function') {
throw new Exception('invalid policy');
}
var _accumulate;
_accumulate = function (accumulated, value) {
_.noop(accumulated);
_accumulate = policy;
if (policy.hasOwnProperty('accumulator')) {
return policy(_.cloneDeep(policy.accumulator), value);
function finalAccumulate(status, value) {
status.accumulated = policy(status.accumulated, value);
return status;
}
var status = {
accumulate: function (status, value) {
status.started = true;
status.accumulate = finalAccumulate;
if (policy.hasOwnProperty('accumulator')) {
status.accumulated = policy(_.cloneDeep(policy.accumulator), value);
} else {
status.accumulated = value;
}
return status;
}
return value;
};
function accumulate(status, value) {
status.started = true;
status.accumulated = _accumulate(status.accumulated, value);
return status;
return status.accumulate(status, value);
}
return reduce(accumulate, {}, _lazyTerminate(policy));
return reduce(accumulate, status, _lazyTerminate(policy));
}

module.exports = {
Expand Down
54 changes: 26 additions & 28 deletions test/reducer/merge.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,19 +44,21 @@ describe('merge', function () {
assert.deepEqual(invoke([{}, {}], reduce), {});
});
it('should return an array if all elements are arrays', function () {
var reduce = r.merge();
assert.deepEqual(invoke([[], []], reduce), []);
assert.ok(_.isArray(invoke([[], []], reduce)));
var reduce = r.merge(),
result = invoke([[], []], reduce);
assert.deepEqual(result, []);
assert.ok(_.isArray(result));
});
it('should return an array if all elements are arrays 1 deep', function () {
var reduce = r.merge();
assert.deepEqual(invoke([{a: []}, {a: []}], reduce), {a: []});
assert.ok(_.isArray(invoke([{a: []}, {a: []}], reduce).a));
var reduce = r.merge(),
result = invoke([{a: []}, {a: []}], reduce);
assert.deepEqual(result, {a: []});
assert.ok(_.isArray(result.a));
});
it('should return an object if elements are mixed object or arrays', function () {
var reduce = r.merge();
assert.ok(!_.isArray(invoke([{a: []}, {a: {}}], reduce), {a: {}}));
assert.ok(!_.isArray(invoke([{a: {}}, {a: []}], reduce), {a: {}}));
assert.ok(!_.isArray(invoke([{a: []}, {a: {}}], reduce)));
assert.ok(!_.isArray(invoke([{a: {}}, {a: []}], reduce)));
});
it('should return an object if elements are mixed object or arrays 1 deep', function () {
var reduce = r.merge();
Expand Down Expand Up @@ -207,40 +209,36 @@ describe('merge', function () {
it('should use the overloaded policies', function () {
var first = {},
second = {},
reduce = r.merge(r.first(), {b: r.last()});
assert.deepEqual(
invoke([{a: first, b: first}, {a: second}], reduce),
{a: first, b: second}
);
reduce = r.merge(r.first(), {b: r.last()}),
result = invoke([{a: first, b: first}, {a: second}], reduce);
assert.ok(_.isPlainObject(result));
assert.deepEqual(result, {a: first, b: second});
});
it('should initialize accumulation if no data is provided', function () {
var first = {},
second = {},
third = {},
reduce = r.merge(r.first(), {b: r.concat()});
assert.deepEqual(
invoke([{a: first}, {a: [second, third]}], reduce),
{a: first, b: []}
);
reduce = r.merge(r.first(), {b: r.concat()}),
result = invoke([{a: first}, {a: [second, third]}], reduce);
assert.ok(_.isPlainObject(result));
assert.deepEqual(result, {a: first, b: []});
});
it('should use the terminate on overloaded policies', function () {
var first = {},
second = {},
third = {},
reduce = r.merge(r.first(), {a: r.flatten()});
assert.deepEqual(
invoke([{a: first}, {a: [second, third]}], reduce),
{a: [first, second, third]}
);
reduce = r.merge(r.first(), {a: r.flatten()}),
result = invoke([{a: first}, {a: [second, third]}], reduce);
assert.ok(_.isPlainObject(result));
assert.deepEqual(result, {a: [first, second, third]});
});
it('should not store overloaded policies if reducer returns undefined', function () {
var first = {},
second = {},
third = {},
reduce = r.merge(r.first(), {a: r.reduce(_.noop)});
assert.deepEqual(
invoke([{a: first}, {a: [second, third]}], reduce),
{}
);
reduce = r.merge(r.first(), {a: r.reduce(_.noop)}),
result = invoke([{a: first}, {a: [second, third]}], reduce);
assert.ok(_.isPlainObject(result));
assert.deepEqual(result, {});
});
});

0 comments on commit fe61d60

Please sign in to comment.