├── .travis.yml
├── .gitignore
├── test
├── perf
│ ├── index-test.js
│ ├── gen.js
│ └── perf-test.js
├── trans
│ ├── index-test.js
│ ├── basic-test.js
│ ├── default-test.js
│ ├── flatten-test.js
│ ├── array-test.js
│ ├── filter-test.js
│ ├── pick-test.js
│ ├── list-test.js
│ ├── remove-test.js
│ ├── object-test.js
│ ├── sort-test.js
│ ├── omit-test.js
│ ├── group-test.js
│ └── map-test.js
└── util.js
├── lib
├── context.js
├── node.js
├── osort.js
├── invoker.js
├── opath.js
├── util.js
├── trav.js
├── ocopy.js
└── index.js
├── package.json
├── CHANGELOG.md
├── LICENSE
├── Makefile
└── README.md
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: node_js
3 | cache:
4 | directories:
5 | - node_modules
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.csv
2 | *.dat
3 | *.gz
4 | *.log
5 | *.out
6 | *.pid
7 | *.rdb
8 | *.seed
9 |
10 | .DS_Store
11 | .stat-test
12 |
13 | NERD_tree_*
14 | lib-cov
15 | logs
16 | node_modules
17 | npm-debug.log
18 | pids
19 | results
20 | tmp
21 |
--------------------------------------------------------------------------------
/test/perf/index-test.js:
--------------------------------------------------------------------------------
1 | var util = require('../util')
2 | , path = require('path');
3 |
4 | describe('trans performance', function () {
5 | [ 'perf-test' ].forEach(function (name) {
6 | require(path.join(process.cwd(), 'test', 'perf', name))(util);
7 | });
8 | });
9 |
--------------------------------------------------------------------------------
/test/trans/index-test.js:
--------------------------------------------------------------------------------
1 | var util = require('../util')
2 | , path = require('path');
3 |
4 | describe('trans', function () {
5 | [
6 | 'basic-test'
7 | , 'map-test'
8 | , 'group-test'
9 | , 'flatten-test'
10 | , 'sort-test'
11 | , 'pick-test'
12 | , 'omit-test'
13 | , 'remove-test'
14 | , 'list-test'
15 | , 'object-test'
16 | , 'array-test'
17 | , 'default-test'
18 | , 'filter-test'
19 | ].forEach(function (name) {
20 | require(path.join(process.cwd(), 'test', 'trans', name))(util);
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/lib/context.js:
--------------------------------------------------------------------------------
1 |
2 | function Context (data) {
3 | data = data || {};
4 | this.indexes = data.indexes || [];
5 | this.index = data.index || 0;
6 | }
7 |
8 | exports.Context = Context;
9 |
10 | Context.prototype.pushIndex = function() {
11 | this.indexes.unshift(this.index);
12 | };
13 |
14 | Context.prototype.popIndex = function() {
15 | this.index = this.indexes.shift();
16 | };
17 |
18 | Context.prototype.setIndex = function(index) {
19 | this.index = index;
20 | };
21 |
22 | Context.prototype.getIndex = function() {
23 | return this.index;
24 | };
25 |
26 | Context.prototype.getIndexes = function() {
27 | return [this.index].concat(this.indexes.slice(0, this.indexes.length - 1));
28 | };
29 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "author": {
3 | "email": "gabesoft@gmail.com",
4 | "name": "Gabriel Adomnicai"
5 | },
6 | "bin": {},
7 | "dependencies": {},
8 | "description": "The ultimate object transformer",
9 | "devDependencies": {
10 | "Faker": "^0.7.2",
11 | "eyes": "^0.1.8",
12 | "mocha": "^2.3.4",
13 | "mocha-better-spec-reporter": "^3.0.1",
14 | "range.js": "^1.1.0"
15 | },
16 | "license": "MIT",
17 | "main": "./lib/index.js",
18 | "name": "trans",
19 | "preferGlobal": false,
20 | "repository": {
21 | "type": "git",
22 | "url": "git@github.com:gabesoft/trans.git"
23 | },
24 | "scripts": {
25 | "test": "node_modules/.bin/mocha -u tdd --check-leaks -R spec test/trans/index-test.js"
26 | },
27 | "version": "0.1.2"
28 | }
29 |
--------------------------------------------------------------------------------
/test/util.js:
--------------------------------------------------------------------------------
1 | exports.trans = require('../lib');
2 | exports.eyes = require('eyes');
3 | exports.assert = require('assert');
4 | exports.truncate = function (x, len) { return x.substring(0, len || 3); };
5 | exports.inspect = function (obj) { exports.eyes.inspect(obj); };
6 | exports.sum = function (xs) { return xs.reduce(function (acc, x) { return x + acc; }, 0); };
7 | exports.mod = function (x, y) { return x % y; };
8 | exports.add = function (x, y) { return x + y; };
9 | exports.gt = function (x, y) { return x > y; };
10 | exports.lt = function (x, y) { return x < y; };
11 | exports.square = function (x) { return x * x; };
12 | exports.stringify = function (obj) {
13 | return JSON.stringify(obj, function (key, val) {
14 | return (typeof val === 'function') ? val + '' : val;
15 | });
16 | };
17 |
18 | exports.eyes.defaults.maxLength = 8192;
19 |
--------------------------------------------------------------------------------
/test/trans/basic-test.js:
--------------------------------------------------------------------------------
1 | module.exports = function (util) {
2 | var trans = util.trans
3 | , assert = util.assert
4 | , square = util.square
5 | , mod = util.mod
6 | , sum = util.sum
7 | , add = util.add;
8 |
9 | describe('value', function () {
10 | it('should return the transformation state', function () {
11 | var o = { a: 1 };
12 | assert.strictEqual(trans(o).value(), o);
13 | });
14 | });
15 |
16 | describe('get', function () {
17 | it('should get the transformation state', function () {
18 | var o = { a: 1 };
19 | trans(o).get(function (e) { assert.strictEqual(e, o); });
20 | });
21 |
22 | it('should allow further chaining', function () {
23 | var o = { a: 1 }
24 | , e = trans(o).get().value();
25 | assert.strictEqual(e, o);
26 | });
27 | });
28 | };
29 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | 0.1.2
2 | -----
3 | Clone date objects properly
4 |
5 | 0.1.1
6 | -----
7 | Fixed tests
8 |
9 | 0.1.0
10 | -----
11 | Added count method
12 |
13 | 0.0.16
14 | -----
15 | Better error messages
16 |
17 | 0.0.15
18 | -----
19 | Added uniq method to filter duplicates
20 |
21 | 0.0.14
22 | -----
23 | More mapff fixes
24 |
25 | 0.0.13
26 | -----
27 | Bug fixes in mapff + documentation + unit tests
28 |
29 | 0.0.12
30 | -----
31 | Documentation fixes 5
32 |
33 | 0.0.11
34 | -----
35 | Documentation fixes 4
36 |
37 | 0.0.10
38 | -----
39 | Documentation fixes 3
40 |
41 | 0.0.9
42 | -----
43 | Documentation fixes 3
44 |
45 | 0.0.8
46 | -----
47 | Documentation fixes 2
48 |
49 | 0.0.7
50 | -----
51 | Documentation fixes
52 |
53 | 0.0.6
54 | -----
55 | Documentation + minor fixes
56 |
57 | 0.0.4
58 | -----
59 | Load eyes module on demand
60 |
61 | 0.0.3
62 | -----
63 | Added filter + unit tests + fixes
64 |
65 | 0.0.2
66 | -----
67 | Added convenience list methods
68 |
69 |
--------------------------------------------------------------------------------
/lib/node.js:
--------------------------------------------------------------------------------
1 | var util = require('./util')
2 | , ensure = util.ensure
3 | , fail = util.fail
4 | , isUndefined = util.isUndefined
5 | , isObject = util.isObject
6 | , isString = util.isString;
7 |
8 | function addField (obj, field, value) {
9 | ensure(field, 'No field specified', obj);
10 | ensure(isObject(obj), 'Could not create field ' + field + ' on', obj);
11 | obj[field] = value;
12 | }
13 |
14 | function Node (parent, field) {
15 | this.parent = parent;
16 | this.field = field;
17 | }
18 |
19 | exports.Node = Node;
20 |
21 | Node.prototype.getValue = function() {
22 | var val = this.parent[this.field];
23 | return isUndefined(val) ? null : val;
24 | };
25 |
26 | Node.prototype.setValue = function (value) {
27 | addField(this.parent, this.field, value);
28 | };
29 |
30 | Node.prototype.getValueOrParent = function() {
31 | var val = this.field ? this.parent[this.field] : this.parent;
32 | return isUndefined(val) ? null : val;
33 | };
34 |
--------------------------------------------------------------------------------
/lib/osort.js:
--------------------------------------------------------------------------------
1 |
2 | exports.createComparer = function(fn, descending) {
3 | if (fn) {
4 | return function (x, y) {
5 | var val = fn(x.criteria, y.criteria);
6 | return val === 0 ? x.index - y.index : val;
7 | };
8 | } else if (descending) {
9 | return function (x, y) {
10 | var cx = x.criteria
11 | , cy = y.criteria;
12 |
13 | if (cx !== cy) {
14 | if (cx > cy || cx === void 0) { return -1; }
15 | if (cx < cy || cy === void 0) { return 1; }
16 | }
17 |
18 | return x.index - y.index;
19 | };
20 | } else {
21 | return function (x, y) {
22 | var cx = x.criteria
23 | , cy = y.criteria;
24 |
25 | if (cx !== cy) {
26 | if (cx > cy || cx === void 0) { return 1; }
27 | if (cx < cy || cy === void 0) { return -1; }
28 | }
29 |
30 | return x.index - y.index;
31 | };
32 | }
33 | };
34 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | This software is released under the MIT license:
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of
4 | this software and associated documentation files (the "Software"), to deal in
5 | the Software without restriction, including without limitation the rights to
6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7 | the Software, and to permit persons to whom the Software is furnished to do so,
8 | subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 |
20 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | default: test
2 |
3 | MODULES = ./node_modules/.bin
4 | MOCHA = $(MODULES)/mocha -u tdd --check-leaks -R mocha-better-spec-reporter
5 | VERSION = $(shell node -pe 'require("./package.json").version')
6 |
7 | all: jshint test
8 |
9 | .PHONY: release test loc clean no_targets__ help
10 |
11 | no_targets__:
12 | help:
13 | @sh -c "$(MAKE) -rpn no_targets__ | awk -F':' '/^[a-zA-Z0-9][^\$$#\/\\t=]*:([^=]|$$)/ {split(\$$1,A,/ /);for(i in A)print A[i]}' | grep -v '__\$$' | grep -v 'Makefile' | grep -v 'make\[1\]' | sort"
14 |
15 | tag:
16 | @git tag -a "v$(VERSION)" -m "Version $(VERSION)"
17 |
18 | tag-push: tag
19 | @git push --tags origin HEAD:master
20 |
21 | test:
22 | @NODE_ENV=test $(MOCHA) test/trans/index-test.js
23 |
24 | test-slow:
25 | @NODE_ENV=test $(MOCHA) test/perf/index-test.js --timeout 10000
26 |
27 | test-all:
28 | @NODE_ENV=test $(MOCHA) test/trans/index-test.js test/perf/index-test.js --timeout 10000
29 |
30 | jshint:
31 | jshint lib/**
32 | jshint test/**
33 |
34 | loc:
35 | @find src/ -name *.js | xargs wc -l
36 |
37 | setup:
38 | @npm install . -d
39 |
40 | clean-dep:
41 | @rm -rf node_modules
42 |
43 |
--------------------------------------------------------------------------------
/lib/invoker.js:
--------------------------------------------------------------------------------
1 | var util = require('./util')
2 | , ensure = util.ensure
3 | , fail = util.fail
4 | , isArray = util.isArray
5 | , isFunction = util.isFunction
6 | , isUndefined = util.isUndefined
7 | , isObject = util.isObject
8 | , isString = util.isString
9 | , toArray = util.toArray;
10 |
11 | function Invoker (context) {
12 | this.context = context;
13 | }
14 |
15 | exports.Invoker = Invoker;
16 |
17 | Invoker.prototype.run = function() {
18 | var args = toArray(arguments)
19 | , context = this.context
20 | , target = args[0]
21 | , fn = args[1]
22 | , rest = args.slice(2);
23 |
24 | if (isFunction(fn)) {
25 | return fn.apply(context, [target].concat(rest));
26 | }
27 | if (isArray(fn)) {
28 | return this.run.apply(this, [target].concat(fn));
29 | }
30 | if (isObject(fn)) {
31 | return fn[target];
32 | }
33 | if (!isString(fn)) {
34 | fail('Could not apply transformer', fn || null);
35 | }
36 |
37 | fn = target[fn];
38 |
39 | return isFunction(fn) ? fn.apply(target, rest) : fn;
40 | };
41 |
--------------------------------------------------------------------------------
/test/trans/default-test.js:
--------------------------------------------------------------------------------
1 | module.exports = function (util) {
2 | var trans = util.trans
3 | , assert = util.assert
4 | , add = util.add
5 | , sum = util.sum
6 | , square = util.square;
7 |
8 | describe('default', function () {
9 | it('should set default values for null or undefined values', function () {
10 | var o = { a: { b: null, c: { }, d: { e: { g: 4 } } } }
11 | , t = trans(o).default('a.b', 1, 'a.c.f', 2, 'a.d.e.f', 5).value();
12 | assert.deepEqual(t, { a: { b: 1, c: { f: 2 }, d: { e: { f: 5, g: 4 } } } });
13 | });
14 |
15 | it('should handle arrays', function () {
16 | var o = { a: [ { b: {} }, { b: { c: null } }, { b: {} } ], e: { d: {}, f: 3 } }
17 | , t = trans(o).default('a.b.c', 2, 'e.d.g', 4).value();
18 | assert.deepEqual(t, {
19 | a: [ { b: { c: 2 } }, { b: { c: 2 } }, { b: { c: 2 } } ]
20 | , e: { d: { g: 4 }, f: 3 }
21 | });
22 | });
23 |
24 | it('should throw if there isn\'t an even number of arguments', function () {
25 | assert.throws(function () {
26 | trans({ a: 1 }).default('a', 1, 'b');
27 | }, /an even number of arguments was expected/i);
28 | });
29 |
30 | it('should throw if asked to set defaults on a primitive value', function () {
31 | assert.throws(function () {
32 | trans({ a: { b: 1, c: null }, d: 1 }).default('d.e', 3);
33 | }, /could not create field/i);
34 | });
35 | });
36 | };
37 |
--------------------------------------------------------------------------------
/lib/opath.js:
--------------------------------------------------------------------------------
1 | var END_DOT = /\.$/
2 | , DOT = '.'
3 | , COL = ':'
4 | , util = require('./util')
5 | , isArray = util.isArray;
6 |
7 | function Path (name, iter) {
8 | var parts = null;
9 |
10 | this.full = '';
11 | this.path = [];
12 | this.iter = !!iter;
13 | this.meta = [];
14 |
15 | if (isArray(name)) {
16 | this.full = name.join(DOT);
17 | this.path = name;
18 | } else if (name) {
19 | parts = name.split(COL);
20 | this.full = parts[0].replace(END_DOT, '');
21 | this.path = this.full.split(DOT).filter(Boolean);
22 | this.iter = END_DOT.test(parts[0]);
23 | this.meta = parts.slice(1);
24 | }
25 |
26 | this.nil = this.path.length === 0;
27 | this.exists = !this.nil;
28 | this._isPath = true;
29 | }
30 |
31 | exports.ITER = DOT;
32 |
33 | function parse (name) {
34 | return (name || {})._isPath ? name : new Path(name);
35 | }
36 |
37 | exports.parse = parse;
38 |
39 | exports.join = function (p1, p2) {
40 | p1 = parse(p1);
41 | p2 = parse(p2);
42 | return new Path(p1.path.concat(p2.path), p2.iter);
43 | };
44 |
45 | exports.root = function (src, dst) {
46 | src = parse(src);
47 | dst = parse(dst);
48 |
49 | var len = Math.min(src.path.length, dst.path.length)
50 | , i = 0
51 | , r = [];
52 |
53 | for (i = 0; i < len; i++) {
54 | if (src.path[i] === dst.path[i]) {
55 | r.push(src.path[i]);
56 | } else {
57 | break;
58 | }
59 | }
60 |
61 | return {
62 | root : new Path(r)
63 | , src : new Path(src.path.slice(i))
64 | , dst : new Path(dst.path.slice(i))
65 | };
66 | };
67 |
--------------------------------------------------------------------------------
/lib/util.js:
--------------------------------------------------------------------------------
1 | var slice = Array.prototype.slice
2 | , util = require('util')
3 | , isArray = util.isArray
4 | , isFunction = function (x) { return typeof x === 'function'; }
5 | , isUndefined = function (x) { return x === void 0; }
6 | , isObject = function (x) { return Object(x) === x && !isArray(x) && !isFunction(x); }
7 | , isString = function (x) { return typeof x === 'string'; }
8 | , isDate = function (x) { return Object.prototype.toString.call(x) === '[object Date]'; }
9 | , toArray = function (x) { return slice.call(x); };
10 |
11 | function ensure (cond, msg, obj) {
12 | if (!cond) {
13 | fail(msg, obj);
14 | }
15 | }
16 |
17 | function fail (msg, obj) {
18 | if (!isUndefined(obj)) {
19 | if (obj === null) {
20 | msg += ' null';
21 | } else if (isFunction(obj)) {
22 | msg += ' ' + obj;
23 | } else {
24 | try {
25 | msg += ' ';
26 | msg += JSON.stringify(obj);
27 | } catch (e) {
28 | msg += ' ';
29 | msg += obj;
30 | }
31 | }
32 | }
33 | throw new Error(msg);
34 | }
35 |
36 | exports.ensure = ensure;
37 | exports.fail = fail;
38 | exports.isArray = util.isArray;
39 | exports.isFunction = isFunction;
40 | exports.isUndefined = isUndefined;
41 | exports.isObject = isObject;
42 | exports.isDate = isDate;
43 | exports.isString = isString;
44 | exports.toArray = toArray;
45 | exports.default = function (obj, val) { return isUndefined(obj) ? val : obj; };
46 | exports.trueFn = function () { return true; };
47 | exports.idFn = function (o) { return o; };
48 |
--------------------------------------------------------------------------------
/test/trans/flatten-test.js:
--------------------------------------------------------------------------------
1 | module.exports = function (util) {
2 | var trans = util.trans
3 | , assert = util.assert;
4 |
5 | describe('flatten', function () {
6 | it('should flatten an array - shallow', function () {
7 | var o = [ [ [ 1 ], [ 2 ] ], [ [ 1, 2, 3 ], [ 4 ] ] ]
8 | , t = trans(o).flatten().value();
9 | assert.deepEqual(t, [ [ 1 ], [ 2 ], [ 1, 2, 3 ], [ 4 ] ]);
10 | });
11 |
12 | it('should flatten an array - deep', function () {
13 | var o = [ [ [ 1 ], [ 2 ] ], [ [ 1, 2, 3 ], [ 4 ] ] ]
14 | , t = trans(o).flatten(true).value();
15 | assert.deepEqual(t, [ 1, 2, 1, 2, 3, 4 ]);
16 | });
17 |
18 | it('should leave the object unchanged if not an array', function () {
19 | var o = { a: 1 }
20 | , t = trans(o).flatten().value();
21 | assert.strictEqual(t, o);
22 | });
23 | });
24 |
25 | describe('flattenf', function () {
26 | it('should flatten the array at the given field', function () {
27 | var o = { a: { b: 1 }, c: { d: [ [ 'a', 'b' ], [ 'c', 'd', 'e' ], [ 'f' ] ] } }
28 | , t = trans(o).flattenf('c.d').value();
29 | assert.deepEqual(t, { a: { b: 1 }, c: { d: [ 'a', 'b', 'c', 'd', 'e', 'f' ] } });
30 | });
31 | });
32 |
33 | describe('flattenff', function () {
34 | it('should flatten the target value and set it on the destination field', function () {
35 | var o = { a: { b: [ [ 1 ], [ [ 2 ], 3 ], [ [ [ 4 ] ], [ 5 ] ] ] } }
36 | , t = trans(o).flattenff('a.b', 'c', true).value();
37 | assert.deepEqual(t, {
38 | a: { b: [ [ 1 ], [ [ 2 ], 3 ], [ [ [ 4 ] ], [ 5 ] ] ] }
39 | , c: [ 1, 2, 3, 4, 5 ]
40 | });
41 | });
42 | });
43 | };
44 |
--------------------------------------------------------------------------------
/test/perf/gen.js:
--------------------------------------------------------------------------------
1 | var faker = require('faker')
2 | , data = null
3 | , addrCount = 12
4 | , contactCount = 12
5 | , phoneCount = 12
6 | , emailCount = 12;
7 |
8 | function rand (min, max) {
9 | return min + Math.floor(Math.random() * (max - min + 1));
10 | }
11 |
12 | function genlist (count, gen) {
13 | var list = []
14 | , i = 0;
15 |
16 | for (i = 0; i < count; i++) {
17 | list.push(gen());
18 | }
19 |
20 | return list;
21 | }
22 |
23 | function genAddress () {
24 | var addr = faker.Address;
25 | return {
26 | zip : addr.zipCode()
27 | , city : addr.city()
28 | , street : addr.streetAddress()
29 | , suite : addr.secondaryAddress()
30 | , state : addr.usState()
31 | , geo : { lat: addr.latitude(), lng: addr.longitude() }
32 | };
33 | }
34 |
35 | function genContact () {
36 | var name = faker.Name
37 | , phone = faker.PhoneNumber;
38 |
39 | return {
40 | first : name.firstName()
41 | , last : name.lastName()
42 | , phones : genlist(phoneCount, phone.phoneNumber)
43 | , addresses : genlist(addrCount, genAddress)
44 | , emails : genlist(emailCount, function () { return faker.Internet.email(); })
45 | };
46 | }
47 |
48 | function genCompany () {
49 | var co = faker.Company;
50 | return {
51 | name : co.companyName()
52 | , website : faker.Internet.domainName()
53 | , phrase : co.catchPhrase()
54 | , tagLine : co.bs()
55 | , mission : faker.Lorem.paragraph()
56 | , locations : genlist(addrCount, genAddress)
57 | , contacts : genlist(contactCount, genContact)
58 | };
59 | }
60 |
61 | exports.createCompanies = function (count) {
62 | return genlist(count, genCompany);
63 | };
64 |
--------------------------------------------------------------------------------
/lib/trav.js:
--------------------------------------------------------------------------------
1 | var util = require('./util')
2 | , opath = require('./opath')
3 | , Invoker = require('./invoker').Invoker
4 | , Node = require('./node').Node
5 | , ensure = util.ensure
6 | , fail = util.fail
7 | , isArray = util.isArray
8 | , isFunction = util.isFunction
9 | , isUndefined = util.isUndefined
10 | , isObject = util.isObject
11 | , isString = util.isString
12 | , toArray = util.toArray
13 | , trueFn = util.trueFn
14 | , idFn = util.idFn;
15 |
16 | function addField (obj, field, value) {
17 | ensure(field, 'No field specified', obj);
18 | ensure(isObject(obj), 'Could not create field ' + field + ' on', obj);
19 | obj[field] = value;
20 | }
21 |
22 | function Trav (context) {
23 | this.context = context;
24 | this.invoker = new Invoker(context);
25 | }
26 |
27 | exports.Trav = Trav;
28 |
29 | Trav.prototype.loop = function(obj, fnAdd, fnMap) {
30 | var context = this.context
31 | , objects = Object(obj)
32 | , self = this
33 | , result = []
34 | , i = 0
35 | , len = obj.length >>> 0;
36 |
37 | ensure(isArray(obj), 'Array expected but got', obj || null);
38 |
39 | context.pushIndex();
40 | for (i = 0; i < len; i++) {
41 | if (i in objects) {
42 | context.setIndex(i);
43 |
44 | if (fnAdd(objects[i], i)) {
45 | result.push(fnMap(objects[i], i));
46 | }
47 | }
48 | }
49 | context.popIndex();
50 |
51 | return result;
52 | };
53 |
54 | Trav.prototype.map = function(obj, fn) {
55 | return this.loop(obj, trueFn, fn);
56 | };
57 |
58 | Trav.prototype.filter = function(obj, fn) {
59 | return this.loop(obj, fn, idFn);
60 | };
61 |
62 | Trav.prototype.walkValue = function(obj, fields, create) {
63 | return this.walk(obj, fields, create, function (node) {
64 | return node.getValue();
65 | });
66 | };
67 |
68 | Trav.prototype.walk = function(obj, fields, create, fn) {
69 | if (!obj) { return null; }
70 |
71 | var self = this;
72 |
73 | if (isArray(obj)) {
74 | return self.map(obj, function (o) { return self.walk(o, fields, create, fn); });
75 | }
76 | if (!fields || fields.length === 0) {
77 | return fn(new Node(obj, null));
78 | }
79 | if (fields.length === 1) {
80 | return fn(new Node(obj, fields[0]));
81 | }
82 |
83 | if (create && !obj.hasOwnProperty(fields[0])) {
84 | addField(obj, fields[0], {});
85 | }
86 |
87 | if (obj.hasOwnProperty(fields[0])) {
88 | return self.walk(obj[fields[0]], fields.slice(1), create, fn);
89 | }
90 |
91 | return null;
92 | };
93 |
94 | Trav.prototype.transform = function(obj, funs) {
95 | if (!funs || funs.length === 0) { return obj; }
96 |
97 | var next = funs[0]
98 | , rest = funs.slice(1)
99 | , self = this
100 | , invoker = this.invoker
101 | , result = null;
102 |
103 | if (next === opath.ITER) {
104 | result = self.map(obj, function (o) {
105 | return self.transform(o, rest);
106 | });
107 | } else {
108 | obj = invoker.run(obj, next);
109 | result = self.transform(obj, rest);
110 | }
111 |
112 | return result;
113 | };
114 |
--------------------------------------------------------------------------------
/lib/ocopy.js:
--------------------------------------------------------------------------------
1 | var util = require('./util')
2 | , opath = require('./opath')
3 | , ensure = util.ensure
4 | , fail = util.fail
5 | , isArray = util.isArray
6 | , isFunction = util.isFunction
7 | , isDate = util.isDate
8 | , isUndefined = util.isUndefined
9 | , isObject = util.isObject
10 | , isString = util.isString
11 | , toArray = util.toArray;
12 |
13 | function fieldTree (fields) {
14 | var root = {};
15 |
16 | fields.forEach(function (field) {
17 | var r = root
18 | , p = opath.parse(field);
19 | p.path.forEach(function (part) {
20 | if (!r[part]) {
21 | r[part] = { _end_: true };
22 | }
23 | if (r._end_) {
24 | r._end_ = false;
25 | }
26 | r = r[part];
27 | });
28 | });
29 |
30 | return root;
31 | }
32 |
33 | function removeFields (obj, removelist) {
34 | var keys = Object.keys(removelist)
35 | , i = 0
36 | , key = null
37 | , len = keys.length;
38 |
39 | for (i = 0; i < len; i++) {
40 | key = keys[i];
41 |
42 | if (!(key in obj)) {
43 | continue;
44 | }
45 |
46 | if (removelist[key]._end_) {
47 | delete obj[key];
48 | } else if (removelist[key]) {
49 | copyObject(removeFields, obj[key], removelist[key]);
50 | }
51 | }
52 | }
53 |
54 | function copyBlack (obj, blacklist, parents) {
55 | blacklist = blacklist || {};
56 |
57 | var clone = {}
58 | , keys = Object.keys(obj)
59 | , i = 0
60 | , key = null
61 | , end = null
62 | , len = keys.length;
63 |
64 | parents.push(obj);
65 | for (i = 0; i < len; i++) {
66 | key = keys[i];
67 | end = blacklist[key] && blacklist[key]._end_;
68 |
69 | if (end || parents.indexOf(obj[key]) !== -1) {
70 | continue;
71 | }
72 |
73 | clone[key] = copyObject(copyBlack, obj[key], blacklist[key], parents);
74 | }
75 | parents.pop(obj);
76 |
77 | return clone;
78 | }
79 |
80 | function copyWhite (obj, whitelist) {
81 | var keys = Object.keys(whitelist)
82 | , clone = {}
83 | , i = 0
84 | , key = null
85 | , len = keys.length;
86 |
87 | for (i = 0; i < len; i++) {
88 | key = keys[i];
89 |
90 | if(!(key in obj)) {
91 | continue;
92 | }
93 |
94 | clone[key] = copyObject(copyWhite, obj[key], whitelist[key]);
95 | }
96 |
97 | return clone;
98 | }
99 |
100 | function copyObject (copyFn, obj, fields, parents) {
101 | if (isArray(obj)) {
102 | return obj.map(function (o) { return copyObject(copyFn, o, fields, parents); });
103 | } else if (isDate(obj)) {
104 | return new Date(obj.getTime());
105 | } else if (isObject(obj)) {
106 | return copyFn(obj, fields, parents);
107 | } else {
108 | return obj;
109 | }
110 | }
111 |
112 | exports.copyWhite = function (obj, fields) {
113 | return copyObject(copyWhite, obj, fieldTree(fields));
114 | };
115 |
116 | exports.copyBlack = function (obj, fields) {
117 | return copyObject(copyBlack, obj, fieldTree(fields), []);
118 | };
119 |
120 | exports.removeFields = function (obj, fields) {
121 | return copyObject(removeFields, obj, fieldTree(fields));
122 | };
123 |
--------------------------------------------------------------------------------
/test/trans/array-test.js:
--------------------------------------------------------------------------------
1 | module.exports = function (util) {
2 | var trans = util.trans
3 | , assert = util.assert
4 | , add = util.add
5 | , sum = util.sum
6 | , square = util.square;
7 |
8 | describe('array', function () {
9 | it('should turn an object into an array', function () {
10 | var o = { a: 1, b: 2, c: 3 }
11 | , t = trans(o).array().value();
12 | assert.deepEqual(t, [
13 | { key: 'a', value: 1 }
14 | , { key: 'b', value: 2 }
15 | , { key: 'c', value: 3 }
16 | ]);
17 | });
18 |
19 | it('should be able to specify the key and value names', function () {
20 | var o = { a: { b: 'A' }, c: { b: 'C' }, d: { b: 'D' } }
21 | , t = trans(o).array('letter', 'obj').value();
22 | assert.deepEqual(t, [
23 | { letter: 'a', obj: { b: 'A' } }
24 | , { letter: 'c', obj: { b: 'C' } }
25 | , { letter: 'd', obj: { b: 'D' } }
26 | ]);
27 | });
28 |
29 | it('should be able to get only keys', function () {
30 | var o = { a: 1, b: 2, c: 3 }
31 | , t = trans(o).array().map('.', 'key').value();
32 | assert.deepEqual(t, [ 'a', 'b', 'c' ]);
33 | });
34 |
35 | it('should be able to get only values', function () {
36 | var o = { a: 1, b: 2, c: 3 }
37 | , t = trans(o).array().map('.', 'value').value();
38 | assert.deepEqual(t, [ 1, 2, 3 ]);
39 | });
40 |
41 | it('should arrayify every object in an array', function () {
42 | var o = [ { a: 1, b: 2 }, { a: 3, b: 4, c: 5 }, { d: 6 } ]
43 | , t = trans(o).array().value();
44 | assert.deepEqual(t, [
45 | [ { key: 'a', value: 1 }, { key: 'b', value: 2 } ]
46 | , [ { key: 'a', value: 3 }, { key: 'b', value: 4 }, { key: 'c', value: 5 } ]
47 | , [ { key: 'd', value: 6 } ]
48 | ]);
49 | });
50 |
51 | it('should work with nested arrays', function () {
52 | var o = [ [ { a: 1, c: 3 }, { a: 1 } ], [ { a: 1, b: 2 }, { c: 3 } ] ]
53 | , t = trans(o).array('k', 'v').value();
54 | assert.deepEqual(t, [
55 | [ [ { k: 'a', v: 1 }, { k: 'c', v: 3 } ], [ { k: 'a', v: 1 } ] ]
56 | , [ [ { k: 'a', v: 1 }, { k: 'b', v: 2 } ], [ { k: 'c', v: 3 } ] ]
57 | ]);
58 | });
59 |
60 | it('should be reversible with object', function () {
61 | var o = { a: 1, b: 2, c: 3 }
62 | , t = trans(o).array().object('key', 'value').value();
63 | assert.deepEqual(t, o);
64 | });
65 |
66 | it('should throw if the target is not an object 1', function () {
67 | assert.throws(function () {
68 | trans('bad').array();
69 | }, /object expected/i);
70 | });
71 |
72 | it('should throw if the target is not an object 2', function () {
73 | assert.throws(function () {
74 | trans([ 'abc', 'def' ]).array();
75 | }, /object expected/i);
76 | });
77 | });
78 |
79 | describe('arrayf', function () {
80 | it('should arrayify the object at the given field', function () {
81 | var o = { a: { d: { b: 1, c: 2 } } }
82 | , t = trans(o).arrayf('a.d', 'k', 'v').value();
83 | assert.deepEqual(t, {
84 | a: { d: [ { k: 'b', v: 1 }, { k: 'c', v: 2 } ] }
85 | });
86 | });
87 | });
88 |
89 | describe('arrayff', function () {
90 | it('should arrayify the source object and set it on the destination', function () {
91 | var o = { a: { b: { c: 1, d: 2 } }, c: 'ready' }
92 | , t = trans(o).arrayff('a.b', 'c', 'k', 'v').value();
93 | assert.deepEqual(t, {
94 | a: { b: { c: 1, d: 2 } }, c: [ { k: 'c', v: 1 }, { k: 'd', v: 2 } ]
95 | });
96 | });
97 | });
98 | };
99 |
--------------------------------------------------------------------------------
/test/trans/filter-test.js:
--------------------------------------------------------------------------------
1 | module.exports = function (util) {
2 | var trans = util.trans
3 | , assert = util.assert
4 | , square = util.square
5 | , gt = util.gt
6 | , lt = util.lt
7 | , mod = util.mod
8 | , sum = util.sum
9 | , add = util.add;
10 |
11 | describe('filter', function () {
12 | it('should filter by the specified field', function () {
13 | var o = [ { a: { b: 1 } }, { a: { b: 0 } }, { a: { b: 3 } } ]
14 | , t = trans(o).filter('a.b').value();
15 | assert.deepEqual(t, [ { a: { b: 1 } }, { a: { b: 3 } } ]);
16 | });
17 |
18 | it('should filter by the specified field inverted', function () {
19 | var o = [ { a: { b: 1 } }, { a: { b: 0 } }, { a: { b: 3 } } ]
20 | , t = trans(o).filter('a.b:invert').value();
21 | assert.deepEqual(t, [ { a: { b: 0 } }]);
22 | });
23 |
24 | it('should filter by the specified field and predicate', function () {
25 | var o = [ { a: [ 1, 2 ] }, { a: [ 3, 6 ] }, { a: [ 1, 4 ] }, { a: [ 1, 5 ] } ]
26 | , t = trans(o).filter('a', sum, [mod, 3], Boolean).value();
27 | assert.deepEqual(t, [ { a: [ 1, 4 ] } ]); });
28 |
29 | it('should handle nested arrays 1', function () {
30 | var o = [
31 | { a: { b: [ { c: 1 }, { c: 2 }, { c: 3 } ] } }
32 | , { a: { b: [ { c: 5 }, { c: 10 }, { c: 15 } ] } }
33 | , { a: { b: [ { c: 100 }, { c: 200 }, { c: 300 } ] } }
34 | , { a: { b: [ { c: 7 }, { c: 13 }, { c: 19 } ] } } ]
35 | , t = trans(o).filter('a.b.c', sum, [gt, 35]).value();
36 | assert.deepEqual(t, [
37 | { a: { b: [ { c: 100 }, { c: 200 }, { c: 300 } ] } }
38 | , { a: { b: [ { c: 7 }, { c: 13 }, { c: 19 } ] } } ]);
39 | });
40 |
41 | it('should handle nested arrays 2', function () {
42 | var o = [
43 | [ { a: [ { b: 'a' }, { b: 'c' } ] }, { a: [ { b: 'b' } ] }, { a: [ { b: 'c1' }, { b: 'c2' }, { b: 'c3' } ] } ]
44 | , [ { a: [ { b: 'u' } ] }, { a: [ { b: 's' }, { b: 'q' } ] }, { a: [ { b: 't' } ] } ]
45 | , [ { a: [ { b: 'p' } ] }, { a: [ { b: 'q' } ] }, { a: [ { b: 'r' } ] } ]
46 | , [ { a: [ { b: 'x' } ] }, { a: [ { b: 'y' }, { b: 'yy' }, { b: 'yyy' } ] }, { a: [ { b: 'z' } ] } ]
47 | ]
48 | , t = trans(o).filter('a.b', trans
49 | , 'flatten', 'value', ['join', ''], ['match', /cbc1|yyyz/]).value();
50 | assert.deepEqual(t, [
51 | [ { a: [ { b: 'a' }, { b: 'c' } ] }, { a: [ { b: 'b' } ] }, { a: [ { b: 'c1' }, { b: 'c2' }, { b: 'c3' } ] } ]
52 | , [ { a: [ { b: 'x' } ] }, { a: [ { b: 'y' }, { b: 'yy' }, { b: 'yyy' } ] }, { a: [ { b: 'z' } ] } ]
53 | ]);
54 | });
55 |
56 | it('should handle nested arrays 3', function () {
57 | var o = [
58 | [ { a: [ { b: [ 1 ] }, { b: [ 2, 3 ] }, ] }, { a: [ { b: [ 5 ] } ] } ]
59 | , [ { a: [ { b: [ 10, 11, 23, 35 ] }, { b: [ 25, 3 ] }, ] }, { a: [ { b: [ 13 ] } ] } ]
60 | , [ { a: [ { b: [ 36 ] }, { b: [ 2, 3 ] }, ] }, { a: [ { b: [ 18, 19, 20 ] } ] } ]
61 | , [ { a: [ { b: [ 100, 99, 98 ] }, { b: [ 2, 3 ] }, ] }, { a: [ { b: [ 28, 19, 20 ] } ] } ]
62 | ]
63 | , t = trans(o).filter('a.b', trans, ['flatten', true], 'value', sum, [lt, 100]).value();
64 | assert.deepEqual(t, [
65 | [ { a: [ { b: [ 1 ] }, { b: [ 2, 3 ] }, ] }, { a: [ { b: [ 5 ] } ] } ]
66 | , [ { a: [ { b: [ 36 ] }, { b: [ 2, 3 ] }, ] }, { a: [ { b: [ 18, 19, 20 ] } ] } ]
67 | ]);
68 | });
69 |
70 | it('should make the array index available to the predicate', function () {
71 | var o = [ 1, 2, 3, 4, 5, 6, 7, 7 ]
72 | , t = trans(o).filter(null, function (x) { return this.index % 2 === 1; }).value();
73 | assert.deepEqual(t, [ 2, 4, 6, 7 ]);
74 | });
75 |
76 | it('should throw if the filter target is not an array', function () {
77 | assert.throws(function() {
78 | trans({ a: 1 }).filter('a');
79 | }, /the filter target is not an array/i);
80 | });
81 | });
82 |
83 | describe('filterf', function () {
84 | it('should filter the array at the given field', function () {
85 | var o = { a: [ 1, 2, 1, 1, 4, 5 ] }
86 | , t = trans(o).filterf('a', null, [mod, 2]).value();
87 | assert.deepEqual(t, { a: [ 1, 1, 1, 5 ] });
88 | });
89 | });
90 |
91 | describe('filterff', function () {
92 | it('should filter the array at the source field and set it on the destination', function () {
93 | var o = { a: [ 1, 2, 1, 1, 4, 5 ] }
94 | , t = trans(o).filterff('a', 'b', null, [mod, 2]).value();
95 | assert.deepEqual(t, { a: [ 1, 2, 1, 1, 4, 5 ], b: [ 1, 1, 1, 5 ] });
96 | });
97 | });
98 | };
99 |
--------------------------------------------------------------------------------
/test/perf/perf-test.js:
--------------------------------------------------------------------------------
1 | module.exports = function (util) {
2 | var trans = util.trans
3 | , faker = require('faker')
4 | , gen = require('./gen')
5 | , assert = util.assert
6 | , square = util.square
7 | , mod = util.mod
8 | , sum = util.sum
9 | , add = util.add
10 | , data = null;
11 |
12 | function inspectGroup (group, full) {
13 | if (full) {
14 | trans(group)
15 | .map('.', function (g) { return { key: g.key, count: g.value.length }; })
16 | .inspect();
17 | }
18 | util.inspect(group.length);
19 | }
20 |
21 | beforeEach(function () {
22 | data = gen.createCompanies(500);
23 | });
24 |
25 | describe('group', function () {
26 | it('should group companies by contact state', function () {
27 | var t = trans(data)
28 | .group('locations.state.')
29 | .value();
30 | inspectGroup(t);
31 | });
32 |
33 | it('should group companies by contacts emails', function () {
34 | var t = trans(data)
35 | .mapff('contacts.emails.', 'emails')
36 | .flattenf('emails')
37 | .group('emails.')
38 | .value();
39 | inspectGroup(t);
40 | });
41 |
42 | it('should group companies by contacts phone numbers', function () {
43 | var t = trans(data)
44 | .mapff('contacts.phones', 'phones')
45 | .flattenf('phones')
46 | .group('phones.')
47 | .value();
48 | inspectGroup(t);
49 | });
50 |
51 | it('should group companies by contacts latitude', function () {
52 | var t = trans(data)
53 | .mapff('contacts.addresses.geo.lat', 'lat')
54 | .flattenf('lat', true)
55 | .group('lat.')
56 | .value();
57 | inspectGroup(t);
58 | });
59 |
60 | it('should group by the contacts geo locations', function () {
61 | var t = trans(data)
62 | .mapff('contacts.addresses.geo', 'geo', '.', function (geo) {
63 | return Math.floor(parseFloat(geo.lat) + parseFloat(geo.lng));
64 | })
65 | .flattenf('geo', true)
66 | .group('geo.')
67 | .value();
68 | inspectGroup(t);
69 | });
70 | });
71 |
72 | describe('map', function () {
73 | it('should extract contacts latitude', function () {
74 | var t = trans(data)
75 | .map('.', 'contacts', '.', 'addresses', '.', 'geo', 'lat')
76 | .flatten(true)
77 | .value();
78 | util.inspect(t.length);
79 | });
80 | });
81 |
82 | describe('mapf', function () {
83 | it('should replace the geo field with a location field on each contact address', function () {
84 | var t = trans(data)
85 | .mapf('contacts.addresses.geo', function (geo) { return geo.lat + ':' + geo.lng; })
86 | .value();
87 | util.inspect(t.length);
88 | });
89 | });
90 |
91 | describe('mapff', function () {
92 | it('should create a location field on each contact', function () {
93 | var t = trans(data)
94 | .mapff('contacts.addresses.geo', 'contacts.loc', '.', function (geo) { return geo.lat + ':' + geo.lng; })
95 | .mapf('contacts.loc', ['join', ', '])
96 | .pluck('contacts.loc')
97 | .flatten()
98 | .value();
99 | util.inspect(t.length);
100 | });
101 |
102 | it('should map the geo field to the sum of lat, long', function () {
103 | trans(data)
104 | .mapff('contacts.addresses.geo', 'contacts.addresses.sum', function (geo) {
105 | return this.indexes + ':' + geo.lat + geo.lng;
106 | })
107 | .value();
108 | });
109 |
110 | it('should map the city field to a city slug field on every contact address', function () {
111 | var t = trans(data)
112 | .mapff('contacts.addresses.city', 'contacts.addresses.citySlug', faker.Helpers.slugify)
113 | .pluck('contacts.addresses.citySlug')
114 | .flatten(true)
115 | .value();
116 | util.inspect(t.length);
117 | });
118 | });
119 |
120 | describe('remove', function () {
121 | it('should remove items from the contacts addresses', function () {
122 | trans(data)
123 | .remove('contacts.addresses.geo.lat', 'contacts.addresses.suite')
124 | .value();
125 | });
126 | });
127 |
128 | describe('omit', function () {
129 | it('should remove items from the contacts addresses', function () {
130 | trans(data)
131 | .omit('contacts.addresses.geo.lat', 'contacts.addresses.suite')
132 | .value();
133 | });
134 | });
135 |
136 | describe('copy', function () {
137 | it('should deep copy the data object', function () {
138 | trans(data).copy();
139 | });
140 | });
141 |
142 | describe('sort', function () {
143 | it('should sort the companies by first location latitude', function () {
144 | trans(data)
145 | .sortf('locations', 'geo.lat')
146 | .firstf('locations')
147 | .sort('locations.geo.lat');
148 | });
149 | });
150 |
151 | describe('object', function () {
152 | it('should create an object indexed by the contact names', function () {
153 | var t = trans(data)
154 | .pluck('contacts')
155 | .flatten()
156 | .object(null, null, function (c) {
157 | return c.first + '-' + c.last + '-' + this.index;
158 | })
159 | .value();
160 |
161 | util.inspect(trans(t).array().value().length);
162 | });
163 |
164 | it('should create an object indexed by the contact latitude', function () {
165 | var t = trans(data)
166 | .mapff('contacts.addresses.geo.lat', 'lat')
167 | .flattenf('lat', true)
168 | .object('lat.')
169 | .value();
170 | util.inspect(trans(t).array().value().length);
171 | });
172 | });
173 |
174 | describe('filter', function () {
175 | it('should filter companies that have contacts in california', function () {
176 | var t = trans(data)
177 | .filter('contacts.addresses.state', trans, ['flatten', true], 'value', 'join', ['match', /cali/i])
178 | .value();
179 | util.inspect(t.length);
180 | });
181 |
182 | it('should filter companies that have websites ending in .com', function () {
183 | var t = trans(data)
184 | .filter('website', ['match', /\.com$/i])
185 | .value();
186 | util.inspect(t.length);
187 | });
188 | });
189 |
190 | describe('default', function () {
191 | it('should set the geo defaults', function () {
192 | trans(data)
193 | .default('contacts.addresses.geo.lat', 1, 'contacts.addresses.geo.lng', 2)
194 | .value();
195 | });
196 | });
197 |
198 | describe('uniq', function () {
199 | it('should get all contacts states distinct', function () {
200 | var before = 0, after = 0;
201 | trans(data)
202 | .pluck('contacts.addresses.state')
203 | .flatten(true)
204 | .get(function (states) { before = states.length; })
205 | .uniq()
206 | .get(function (states) { after = states.length; })
207 | .value();
208 | util.inspect([ before, after ]);
209 | });
210 | });
211 | };
212 |
--------------------------------------------------------------------------------
/test/trans/pick-test.js:
--------------------------------------------------------------------------------
1 | module.exports = function (util) {
2 | var trans = util.trans
3 | , assert = util.assert
4 | , square = util.square
5 | , mod = util.mod
6 | , sum = util.sum
7 | , add = util.add;
8 |
9 | describe('pick', function () {
10 | it('should keep only the specified fields', function () {
11 | var o = {
12 | a: {
13 | b: 1
14 | , c: 2
15 | , p: { d: 3, e: 4 }
16 | , q: { g: 5, h: 6, f: { k: 8, l: 10, r: 22 } }
17 | }
18 | , m: { n: 11 }
19 | }
20 | , t = trans(o).pick('a.c', 'a.p.d', 'a.q.h', 'a.q.f.k', 'a.q.f.l').value();
21 | assert.deepEqual(t, {
22 | a: {
23 | c: 2
24 | , p: { d: 3 }
25 | , q: { h: 6, f: { k: 8, l: 10 } }
26 | }
27 | });
28 | });
29 |
30 | it('should keep only the specified field on all objects in an array', function () {
31 | var o = [
32 | { a: { b: 'a', c: 'b' }, d: { e: 1, f: 2 } }
33 | , { a: { b: 'b', c: 'e' }, d: { e: 2, f: 3 } }
34 | , { a: { b: 'c', c: 'f' }, d: { e: 3, f: 4 } }
35 | , { a: { b: 'd', c: 'g' }, d: { e: 4, f: 5 } }
36 | ]
37 | , t = trans(o).pick('d.e').value();
38 | assert.deepEqual(t, [
39 | { d: { e: 1 } }
40 | , { d: { e: 2 } }
41 | , { d: { e: 3 } }
42 | , { d: { e: 4 } }
43 | ]);
44 | });
45 |
46 | it('should handle missing keys', function () {
47 | var o = [ { a: { b: 1, c: 2 } }, { a: { c: 3 } }, {} ]
48 | , t = trans(o).pick('a.b').value();
49 | assert.deepEqual(t, [ { a: { b: 1 } }, { a: {} }, {} ]);
50 | });
51 |
52 | it('should handle functions', function () {
53 | var o = { a: { b: 1, c: function () { return 1; } }, e: 2, f: 3 }
54 | , t = trans(o).pick('a.c', 'f').value();
55 | assert.strictEqual(
56 | util.stringify(t)
57 | , util.stringify({ a: { c: function () { return 1; } }, f: 3 }));
58 | });
59 |
60 | it('should work with nested arrays 1', function () {
61 | var o = [
62 | { a: { b: [ { c: 10, d: 10 }, { c: 20, d: 20 } ], d: 'a', e: [ { f: 2 } ] } }
63 | , { a: { b: [ { c: 11, d: 11 }, { c: 12, d: 12 } ], d: 'b', e: [ { f: 2 } ] } }
64 | , { a: { b: [ { c: 21, d: 21 }, { c: 22, d: 22 } ], d: 'c', e: [ { f: 2 } ] } }
65 | ]
66 | , t = trans(o).pick('a.b.d', 'a.d').value();
67 | assert.deepEqual(t, [
68 | { a: { b: [ { d: 10 }, { d: 20 } ], d: 'a' } }
69 | , { a: { b: [ { d: 11 }, { d: 12 } ], d: 'b' } }
70 | , { a: { b: [ { d: 21 }, { d: 22 } ], d: 'c' } }
71 | ]);
72 | });
73 |
74 | it('should handle primitives', function () {
75 | var o = { a: { b: 1 } }
76 | , t = trans(o).pick('a.b.c').value();
77 | assert.deepEqual(t, o);
78 | });
79 |
80 | it('should work with nested arrays 2', function () {
81 | var o = [ [
82 | { a: [
83 | { b: 1, c: [
84 | { d: 1, e: 2, f: { g: 1, h: 2 } }, { d: 1, e: 2, f: { g: 1, h: 2 } } ]
85 | }
86 | , { b: 2, c: [
87 | { d: 2, e: 3, f: { g: 2, h: 3 } }, { d: 2, e: 3, f: { g: 2, h: 3 } } ]
88 | }
89 | , { b: 2, c: [
90 | { d: 2, e: 3, f: { g: 2, h: 3 } }, { d: 2, e: 3, f: { g: 2, h: 3 } } ]
91 | } ]
92 | , b: 4
93 | }
94 | , { a: [
95 | { b: 1, c: [
96 | { d: 1, e: 2, f: { g: 1, h: 2 } }, { d: 1, e: 2, f: { g: 1, h: 2 } } ]
97 | }
98 | , { b: 2, c: [
99 | { d: 2, e: 3, f: { g: 2, h: 3 } }, { d: 2, e: 3, f: { g: 2, h: 3 } } ]
100 | }
101 | , { b: 2, c: [
102 | { d: 2, e: 3, f: { g: 2, h: 3 } }, { d: 2, e: 3, f: { g: 2, h: 3 } } ]
103 | } ]
104 | , b: 4
105 | }]
106 | , [
107 | { a: [
108 | { b: 1, c: [
109 | { d: 1, e: 2, f: { g: 1, h: 2 } }, { d: 1, e: 2, f: { g: 1, h: 2 } } ]
110 | }
111 | , { b: 2, c: [
112 | { d: 2, e: 3, f: { g: 2, h: 3 } }, { d: 2, e: 3, f: { g: 2, h: 3 } } ]
113 | }
114 | , { b: 2, c: [
115 | { d: 2, e: 3, f: { g: 2, h: 3 } }, { d: 2, e: 3, f: { g: 2, h: 3 } } ]
116 | } ]
117 | , b: 4
118 | }
119 | , { a: [
120 | { b: 1, c: [
121 | { d: 1, e: 2, f: { g: 1, h: 2 } }, { d: 1, e: 2, f: { g: 1, h: 2 } } ]
122 | }
123 | , { b: 2, c: [
124 | { d: 2, e: 3, f: { g: 2, h: 3 } }, { d: 2, e: 3, f: { g: 2, h: 3 } } ]
125 | }
126 | , { b: 2, c: [
127 | { d: 2, e: 3, f: { g: 2, h: 3 } }, { d: 2, e: 3, f: { g: 2, h: 3 } } ]
128 | } ]
129 | , b: 4
130 | }]]
131 | , t = trans(o).pick('a.c.e', 'a.c.f.h').value();
132 | assert.deepEqual(t, [
133 | [
134 | { a: [
135 | { c: [ { e: 2, f: { h: 2 } }, { e: 2, f: { h: 2 } } ] }
136 | , { c: [ { e: 3, f: { h: 3 } }, { e: 3, f: { h: 3 } } ] }
137 | , { c: [ { e: 3, f: { h: 3 } }, { e: 3, f: { h: 3 } } ] } ]
138 | }
139 | , { a: [
140 | { c: [ { e: 2, f: { h: 2 } }, { e: 2, f: { h: 2 } } ] }
141 | , { c: [ { e: 3, f: { h: 3 } }, { e: 3, f: { h: 3 } } ] }
142 | , { c: [ { e: 3, f: { h: 3 } }, { e: 3, f: { h: 3 } } ] } ]
143 | }
144 | ]
145 | , [
146 | { a: [
147 | { c: [ { e: 2, f: { h: 2 } }, { e: 2, f: { h: 2 } } ] }
148 | , { c: [ { e: 3, f: { h: 3 } }, { e: 3, f: { h: 3 } } ] }
149 | , { c: [ { e: 3, f: { h: 3 } }, { e: 3, f: { h: 3 } } ] } ]
150 | }
151 | , { a: [
152 | { c: [ { e: 2, f: { h: 2 } }, { e: 2, f: { h: 2 } } ] }
153 | , { c: [ { e: 3, f: { h: 3 } }, { e: 3, f: { h: 3 } } ] }
154 | , { c: [ { e: 3, f: { h: 3 } }, { e: 3, f: { h: 3 } } ] } ]
155 | }
156 | ] ]);
157 | });
158 | });
159 |
160 | describe('pickf', function () {
161 | it('should apply pick at the given field 1', function () {
162 | var o = { a: 1, b: { c: [ { d: 1, e: 2 }, { d: 3, e: 4 } ], f: 'b' }, g: 'c' }
163 | , t = trans(o).pickf('b.c', 'd').value();
164 | assert.deepEqual(t, { a: 1, b: { c: [ { d: 1 }, { d: 3 } ], f: 'b' }, g: 'c' });
165 | });
166 |
167 | it('should apply pick at the given field 2', function () {
168 | var o = { a: 1, b: { c: [ { d: 1, e: 2 }, { d: 3, e: 4 } ], f: 'b' }, g: 'c' }
169 | , t = trans(o).pickf('b', 'c.d').value();
170 | assert.deepEqual(t, { a: 1, b: { c: [ { d: 1 }, { d: 3 } ] }, g: 'c' });
171 | });
172 | });
173 |
174 | describe('pickff', function () {
175 | it('should apply pick on the target and set it on the destination', function () {
176 | var o = { a: 1, b: { c: [ { d: 1, e: 2 }, { d: 3, e: 4 } ], f: 'b' }, g: 'c' }
177 | , t = trans(o).pickff('b.c', 'b.p', 'd').value();
178 | assert.deepEqual(t, {
179 | a: 1
180 | , b: {
181 | c: [ { d: 1, e: 2 }, { d: 3, e: 4 } ]
182 | , p: [ { d: 1 }, { d: 3 } ]
183 | , f: 'b'
184 | }
185 | , g: 'c'
186 | });
187 | });
188 | });
189 | };
190 |
--------------------------------------------------------------------------------
/test/trans/list-test.js:
--------------------------------------------------------------------------------
1 | module.exports = function (util) {
2 | var trans = util.trans
3 | , assert = util.assert
4 | , add = util.add
5 | , mod = util.mod;
6 |
7 | describe('take', function () {
8 | it('should keep only the specified number of items from an array', function () {
9 | var o = [ 1, 2, 3, 4, 5, 6 ]
10 | , t = trans(o).take(4).value();
11 | assert.deepEqual(t, [1, 2, 3, 4]);
12 | });
13 | });
14 |
15 | describe('takef', function () {
16 | it('should keep only the specified number of items from the target array', function () {
17 | var o = { a: { b: [ { c: 1 }, { c: 2 }, { c: 3 }, { c: 4 }, { c: 5 } ] } }
18 | , t = trans(o).takef('a.b', 3).value();
19 | assert.deepEqual(t, { a: { b: [ { c: 1 }, { c: 2 }, { c: 3 } ] } });
20 | });
21 | });
22 |
23 | describe('skip', function () {
24 | it('should remove the first specified items from an array', function () {
25 | var o = [ 1, 2, 3, 4, 5, 6, 7, 8 ]
26 | , t = trans(o).skip(3).value();
27 | assert.deepEqual(t, [ 4, 5, 6, 7, 8 ]);
28 | });
29 | });
30 |
31 | describe('pluck', function () {
32 | it('should pluck the specified field from all objects in an array', function () {
33 | var o = [ { a: { b: 1, c: 1 } }, { a: { b: 2, c: 'b' } }, { a: { b: 3 } }, { a: { b: 4, d: 'F' } }, { a: { b: 5 } } ]
34 | , t = trans(o).pluck('a.b').value();
35 | assert.deepEqual(t, [ 1, 2, 3, 4, 5 ]);
36 | });
37 |
38 | it('should pluck the specified field and apply transformations', function () {
39 | var o = [ { a: { b: 'aab', c: 1 } }, { a: { b: 'aac', c: 'b' } }, { a: { b: 'foo' } }, { a: { b: 'bar', d: 'F' } }, { a: { b: 'GFK' } } ]
40 | , t = trans(o).pluck('a.b', [ 'charAt', 0 ], 'toUpperCase').value();
41 | assert.deepEqual(t, [ 'A', 'A', 'F', 'B', 'G' ]);
42 | });
43 |
44 | it('should pluck the specified field from a single object', function () {
45 | var o = { a: { b: 1, c: { d: { e: 'ABC', f: 3 } }, g: { h: 10 } }, k: 2 }
46 | , t = trans(o).pluck('a.c.d.e', 'toLowerCase').value();
47 | assert.strictEqual(t, 'abc');
48 | });
49 |
50 | it('should handle multiple arrays', function () {
51 | var o = [
52 | [ { a: [ { b: 1 }, { b: 2 } ] }, { a: [ { b: 3 } ] } ]
53 | , [ { a: [ { b: 10 } ] }, { a: [ { b: 11 }, { b: 12 }, { b: 13 } ] } ] ]
54 | , t = trans(o).pluck('a.b', '.', [add, 5]).value();
55 | assert.deepEqual(t, [ [ [ 6, 7 ], [ 8 ] ], [ [ 15 ], [ 16, 17, 18 ] ] ]);
56 | });
57 | });
58 |
59 | describe('pluckf', function () {
60 | it('should replace the field value with the specified pluck values 1', function () {
61 | var o = { a: [ { b: { c: { d: 1 } } }, { b: { c: { d: 2 } } }, { b: { c: { d: 1 } } } ] }
62 | , t = trans(o).pluckf('a.b', 'c.d').value();
63 | assert.deepEqual(t, { a: [ { b: 1 }, { b: 2 }, { b: 1 } ] });
64 | });
65 |
66 | it('should replace the field value with the specified pluck values 2', function () {
67 | var o = { a: [ { b: { c: { d: 1 } } }, { b: { c: { d: 2 } } }, { b: { c: { d: 1 } } } ] }
68 | , t = trans(o).pluckf('a', 'b.c.d').value();
69 | assert.deepEqual(t, { a: [ 1, 2, 1 ] });
70 | });
71 | });
72 |
73 | describe('first', function () {
74 | it('should keep only the first element in the list', function () {
75 | var o = [ 1, 2, 3, 4 ]
76 | , t = trans(o).first().value();
77 | assert.deepEqual(t, 1);
78 | });
79 |
80 | it('should not modify the original array', function () {
81 | var o = [ 1, 2, 3, 4, 5 ]
82 | , t = trans(o).first().value();
83 | assert.deepEqual(o, [ 1, 2, 3, 4, 5 ]);
84 | });
85 |
86 | it('should handle empty arrays', function () {
87 | var t = trans([]).first().value();
88 | assert.strictEqual(t, null);
89 | });
90 | });
91 |
92 | describe('firstf', function () {
93 | it('should keep only the first element from the target array', function () {
94 | var o = { a: { b: [ { c: 1 }, { c: 2 }, { c: 3 }, { c: 4 }, { c: 5 } ] } }
95 | , t = trans(o).firstf('a.b').value();
96 | assert.deepEqual(t, { a: { b: { c: 1 } } });
97 | });
98 | });
99 |
100 | describe('firstff', function () {
101 | it('should keep only the first element from the target array and set it on the destination', function () {
102 | var o = { a: { b: [ { c: 1 }, { c: 2 }, { c: 3 }, { c: 4 }, { c: 5 } ] } }
103 | , t = trans(o).firstff('a.b', 'a.e').value();
104 | assert.deepEqual(t, { a: {
105 | b: [ { c: 1 }, { c: 2 }, { c: 3 }, { c: 4 }, { c: 5 } ]
106 | , e: { c: 1 }
107 | } });
108 | });
109 | });
110 |
111 | describe('last', function () {
112 | it('should keep only the last element in the list', function () {
113 | var o = [ 1, 2, 3, 4 ]
114 | , t = trans(o).last().value();
115 | assert.deepEqual(t, 4);
116 | });
117 |
118 | it('should not modify the original array', function () {
119 | var o = [ 1, 2, 3, 4, 5 ]
120 | , t = trans(o).last().value();
121 | assert.deepEqual(o, [ 1, 2, 3, 4, 5 ]);
122 | });
123 |
124 | it('should handle empty arrays', function () {
125 | var t = trans([]).last().value();
126 | assert.strictEqual(t, null);
127 | });
128 | });
129 |
130 | describe('lastf', function () {
131 | it('should keep only the last element from the target array', function () {
132 | var o = { a: { b: [ { c: 1 }, { c: 2 }, { c: 3 }, { c: 4 }, { c: 5 } ] } }
133 | , t = trans(o).lastf('a.b').value();
134 | assert.deepEqual(t, { a: { b: { c: 5 } } });
135 | });
136 | });
137 |
138 | describe('firstff', function () {
139 | it('should keep only the last element from the target array and set it on the destination', function () {
140 | var o = { a: { b: [ { c: 1 }, { c: 2 }, { c: 3 }, { c: 4 }, { c: 5 } ] } }
141 | , t = trans(o).lastff('a.b', 'a.e').value();
142 | assert.deepEqual(t, { a: {
143 | b: [ { c: 1 }, { c: 2 }, { c: 3 }, { c: 4 }, { c: 5 } ]
144 | , e: { c: 5 }
145 | } });
146 | });
147 | });
148 |
149 | describe('uniq', function () {
150 | it('should remove all duplicates according to the specified field', function () {
151 | var o = [ { a: { b: 1 } }, { a: { b: '1' } }, { a: { b: 2 } }, { a: { b: 1 } }, { a: { b: 10 } } ]
152 | , t = trans(o).uniq('a.b').value();
153 | assert.deepEqual(t, [
154 | { a: { b: 1 } }, { a: { b: '1' } }, { a: { b: 2 } }, { a: { b: 10 } }
155 | ]);
156 | });
157 |
158 | it('should apply transformers and remove all duplicates', function () {
159 | var o = [ { a: { b: 1 } }, { a: { b: '1' } }, { a: { b: 2 } }, { a: { b: 1 } }, { a: { b: 10 } } ]
160 | , t = trans(o).uniq('a.b', parseInt).value();
161 | assert.deepEqual(t, [
162 | { a: { b: 1 } }, { a: { b: 2 } }, { a: { b: 10 } }
163 | ]);
164 | });
165 |
166 | it('should not remove if the value is null or undefined', function () {
167 | var o = [ { a: { b: 1 } }, { a: {} }, { a: { b: 2 } }, { a: {} }, { a: { b: 1 } } ]
168 | , t = trans(o).uniq('a.b').value();
169 | assert.deepEqual(t, [
170 | { a: { b: 1 } }, { a: {} }, { a: { b: 2 } }, { a: {} }
171 | ]);
172 | });
173 |
174 | it('should remove duplicates in an array of strings', function () {
175 | var o = [ 'a', 'b', 'a', 'a', 'b', 'c', 'd', 'a', 'e' ]
176 | , t = trans(o).uniq().value();
177 | assert.deepEqual(t, [ 'a', 'b', 'c', 'd', 'e' ]);
178 | });
179 | });
180 |
181 | describe('uniqf', function () {
182 | it('should remove duplicates from the target array', function () {
183 | var o = [
184 | { a: { b: [ 1, 1, 3, 4, 4, 11, 12, 99, 9, 3, 4 ] } }
185 | , { a: { b: [ 1, 1, 14, 4 ] } } ]
186 | , t = trans(o).uniqf('a.b', null, [mod, 10]).value();
187 | assert.deepEqual(t, [
188 | { a: { b: [ 1, 3, 4, 12, 99 ] } }
189 | , { a: { b: [ 1, 14 ] } } ]);
190 | });
191 | });
192 |
193 | describe('uniqff', function () {
194 | it('should remove duplicates from the target array and set it on the destination', function () {
195 | var o = { a: [ 1, 1, 3 ] }
196 | , t = trans(o).uniqff('a', 'b').value();
197 | assert.deepEqual(t, { a: [ 1, 1, 3 ], b: [ 1, 3 ] });
198 | });
199 | });
200 | };
201 |
--------------------------------------------------------------------------------
/test/trans/remove-test.js:
--------------------------------------------------------------------------------
1 | module.exports = function (util) {
2 | var trans = util.trans
3 | , assert = util.assert
4 | , square = util.square
5 | , mod = util.mod
6 | , sum = util.sum
7 | , add = util.add;
8 |
9 | describe('remove', function () {
10 | it('should remove the specified fields', function () {
11 | var o = {
12 | a: {
13 | b: 1
14 | , c: 2
15 | , p: { d: 3, e: 4 }
16 | , q: { g: 5, h: 6, f: { k: 8, l: 10, r: 22 } }
17 | }
18 | , m: { n: 11 }
19 | }
20 | , t = trans(o).remove('a.b', 'a.p.e', 'a.q.g', 'a.q.f.r', 'm').value();
21 | assert.deepEqual(t, {
22 | a: {
23 | c: 2
24 | , p: { d: 3 }
25 | , q: { h: 6, f: { k: 8, l: 10 } }
26 | }
27 | });
28 | });
29 |
30 | it('should not remove null fields if not specified', function () {
31 | var o = { a: 1, b: { c: 'a', d: null, e: 2 } }
32 | , t = trans(o).remove('b.c').value();
33 | assert.deepEqual(t, { a: 1, b: { d: null, e: 2 } });
34 | });
35 |
36 | it('should not change the object if no fields are specified', function () {
37 | var o = {
38 | a: {
39 | b: 1
40 | , c: 2
41 | , p: { d: 3, e: 4 }
42 | , q: { g: 5, h: 6, f: { k: 8, l: 10, r: 22 } }
43 | , r: [ { s: { t: [ 1 ] } }, { s: { t: [ 2, 3, 4 ] } } ]
44 | }
45 | , m: { n: 11 }
46 | }
47 | , t = trans(o).remove().value();
48 | assert.deepEqual(t, o);
49 | assert.strictEqual(t, o);
50 | });
51 |
52 | it('should remove all the specified fields in place', function () {
53 | var o = {
54 | a: {
55 | b: 1
56 | , c: 2
57 | , p: { d: 3, e: 4 }
58 | , q: { g: 5, h: 6, f: { k: 8, l: 10, r: 22 } }
59 | , r: [ { s: { t: [ 1 ] } }, { s: { t: [ 2, 3, 4 ] } } ]
60 | }
61 | , m: { n: 11 }
62 | }
63 | , t = trans(o).remove('a.c', 'a.p.e', 'a.q.f.k', 'a.r.s.t').value();
64 | assert.deepEqual(o, {
65 | a: {
66 | b: 1
67 | , p: { d: 3 }
68 | , q: { g: 5, h: 6, f: { l: 10, r: 22 } }
69 | , r: [ { s: {} }, { s: {} } ]
70 | }
71 | , m: { n: 11 }
72 | });
73 | assert.deepEqual(t, o);
74 | assert.strictEqual(t, o);
75 | });
76 |
77 | it('should remove the specified fields on all objects in an array', function () {
78 | var o = [
79 | { a: { b: 'a', c: 'b' }, d: { e: 1, f: 2 } }
80 | , { a: { b: 'b', c: 'e' }, d: { e: 2, f: 3 } }
81 | , { a: { b: 'c', c: 'f' }, d: { e: 3, f: 4 } }
82 | , { a: { b: 'd', c: 'g' }, d: { e: 4, f: 5 } }
83 | ]
84 | , t = trans(o).remove('a', 'd.f').value();
85 | assert.deepEqual(t, [
86 | { d: { e: 1 } }
87 | , { d: { e: 2 } }
88 | , { d: { e: 3 } }
89 | , { d: { e: 4 } }
90 | ]);
91 | });
92 |
93 | it('should handle missing keys', function () {
94 | var o = [ { a: { b: 1, c: 2 } }, { a: { c: 3 } }, {} ]
95 | , t = trans(o).remove('a.c').value();
96 | assert.deepEqual(t, [ { a: { b: 1 } }, { a: {} }, {} ]);
97 | });
98 |
99 | it('should handle functions', function () {
100 | var o = { a: { b: 1, c: function () { return 1; } }, e: 2, f: 3 }
101 | , t = trans(o).remove('a.b', 'e').value();
102 | assert.strictEqual(
103 | util.stringify(t)
104 | , util.stringify({ a: { c: function () { return 1; } }, f: 3 }));
105 | });
106 |
107 | it('should remove an array field', function () {
108 | var o = { a: 1, b: [ 1, 2, 4 ] }
109 | , t = trans(o).remove('b').value();
110 | assert.deepEqual(t, { a: 1 });
111 | });
112 |
113 | it('should work with nested arrays 1', function () {
114 | var o = [
115 | { a: { b: [ { c: 10, d: 10 }, { c: 20, d: 20 } ], d: 'a', e: [ { f: 2 } ] } }
116 | , { a: { b: [ { c: 11, d: 11 }, { c: 12, d: 12 } ], d: 'b', e: [ { f: 2 } ] } }
117 | , { a: { b: [ { c: 21, d: 21 }, { c: 22, d: 22 } ], d: 'c', e: [ { f: 2 } ] } }
118 | ]
119 | , t = trans(o).remove('a.b.c', 'a.e').value();
120 | assert.deepEqual(t, [
121 | { a: { b: [ { d: 10 }, { d: 20 } ], d: 'a' } }
122 | , { a: { b: [ { d: 11 }, { d: 12 } ], d: 'b' } }
123 | , { a: { b: [ { d: 21 }, { d: 22 } ], d: 'c' } }
124 | ]);
125 | });
126 |
127 | it('should handle primitives', function () {
128 | var o = { a: { b: 1 } }
129 | , t = trans(o).remove('a.b.c').value();
130 | assert.deepEqual(t, o);
131 | });
132 |
133 | it('should work with nested arrays 2', function () {
134 | var o = [ [
135 | { a: [
136 | { b: 1, c: [
137 | { d: 1, e: 2, f: { g: 1, h: 2 } }, { d: 1, e: 2, f: { g: 1, h: 2 } } ]
138 | }
139 | , { b: 2, c: [
140 | { d: 2, e: 3, f: { g: 2, h: 3 } }, { d: 2, e: 3, f: { g: 2, h: 3 } } ]
141 | }
142 | , { b: 2, c: [
143 | { d: 2, e: 3, f: { g: 2, h: 3 } }, { d: 2, e: 3, f: { g: 2, h: 3 } } ]
144 | } ]
145 | , b: 4
146 | }
147 | , { a: [
148 | { b: 1, c: [
149 | { d: 1, e: 2, f: { g: 1, h: 2 } }, { d: 1, e: 2, f: { g: 1, h: 2 } } ]
150 | }
151 | , { b: 2, c: [
152 | { d: 2, e: 3, f: { g: 2, h: 3 } }, { d: 2, e: 3, f: { g: 2, h: 3 } } ]
153 | }
154 | , { b: 2, c: [
155 | { d: 2, e: 3, f: { g: 2, h: 3 } }, { d: 2, e: 3, f: { g: 2, h: 3 } } ]
156 | } ]
157 | , b: 4
158 | }]
159 | , [
160 | { a: [
161 | { b: 1, c: [
162 | { d: 1, e: 2, f: { g: 1, h: 2 } }, { d: 1, e: 2, f: { g: 1, h: 2 } } ]
163 | }
164 | , { b: 2, c: [
165 | { d: 2, e: 3, f: { g: 2, h: 3 } }, { d: 2, e: 3, f: { g: 2, h: 3 } } ]
166 | }
167 | , { b: 2, c: [
168 | { d: 2, e: 3, f: { g: 2, h: 3 } }, { d: 2, e: 3, f: { g: 2, h: 3 } } ]
169 | } ]
170 | , b: 4
171 | }
172 | , { a: [
173 | { b: 1, c: [
174 | { d: 1, e: 2, f: { g: 1, h: 2 } }, { d: 1, e: 2, f: { g: 1, h: 2 } } ]
175 | }
176 | , { b: 2, c: [
177 | { d: 2, e: 3, f: { g: 2, h: 3 } }, { d: 2, e: 3, f: { g: 2, h: 3 } } ]
178 | }
179 | , { b: 2, c: [
180 | { d: 2, e: 3, f: { g: 2, h: 3 } }, { d: 2, e: 3, f: { g: 2, h: 3 } } ]
181 | } ]
182 | , b: 4
183 | }]]
184 | , t = trans(o).remove('b', 'a.b', 'a.c.d', 'a.c.f.g').value();
185 | assert.deepEqual(t, [
186 | [
187 | { a: [
188 | { c: [ { e: 2, f: { h: 2 } }, { e: 2, f: { h: 2 } } ] }
189 | , { c: [ { e: 3, f: { h: 3 } }, { e: 3, f: { h: 3 } } ] }
190 | , { c: [ { e: 3, f: { h: 3 } }, { e: 3, f: { h: 3 } } ] } ]
191 | }
192 | , { a: [
193 | { c: [ { e: 2, f: { h: 2 } }, { e: 2, f: { h: 2 } } ] }
194 | , { c: [ { e: 3, f: { h: 3 } }, { e: 3, f: { h: 3 } } ] }
195 | , { c: [ { e: 3, f: { h: 3 } }, { e: 3, f: { h: 3 } } ] } ]
196 | }
197 | ]
198 | , [
199 | { a: [
200 | { c: [ { e: 2, f: { h: 2 } }, { e: 2, f: { h: 2 } } ] }
201 | , { c: [ { e: 3, f: { h: 3 } }, { e: 3, f: { h: 3 } } ] }
202 | , { c: [ { e: 3, f: { h: 3 } }, { e: 3, f: { h: 3 } } ] } ]
203 | }
204 | , { a: [
205 | { c: [ { e: 2, f: { h: 2 } }, { e: 2, f: { h: 2 } } ] }
206 | , { c: [ { e: 3, f: { h: 3 } }, { e: 3, f: { h: 3 } } ] }
207 | , { c: [ { e: 3, f: { h: 3 } }, { e: 3, f: { h: 3 } } ] } ]
208 | }
209 | ] ]);
210 | });
211 | });
212 | };
213 |
--------------------------------------------------------------------------------
/test/trans/object-test.js:
--------------------------------------------------------------------------------
1 | module.exports = function (util) {
2 | var trans = util.trans
3 | , assert = util.assert
4 | , add = util.add
5 | , sum = util.sum
6 | , square = util.square;
7 |
8 | describe('object', function () {
9 | it('should objectify an array by the given key', function () {
10 | var o = [ { a: 1, b: 'foo' }, { a: 2, b: 'wow' }, { a: 3, b: 'pow' } ]
11 | , t1 = trans(o).object('a').value()
12 | , t2 = trans(o).object('b').value();
13 |
14 | assert.deepEqual(t1, {
15 | 1: { a: 1, b: 'foo' }
16 | , 2: { a: 2, b: 'wow' }
17 | , 3: { a: 3, b: 'pow' }
18 | });
19 |
20 | assert.deepEqual(t2, {
21 | foo: { a: 1, b: 'foo' }
22 | , wow: { a: 2, b: 'wow' }
23 | , pow: { a: 3, b: 'pow' }
24 | });
25 | });
26 |
27 | it('should objectify an array by the given key and value', function () {
28 | var o = [ { a: 1, b: 'foo' }, { a: 2, b: 'wow' }, { a: 3, b: 'pow' } ]
29 | , t = trans(o).object('b', 'a').value();
30 | assert.deepEqual(t, { foo: 1, wow: 2, pow: 3 });
31 | });
32 |
33 | it('should handle nested keys', function () {
34 | var o = [ { a: { b: { c: 2 } } }, { a: { b: { c: 3 } } }, { a: { b: { c: 4 } } } ]
35 | , t = trans(o).object('a.b.c').value();
36 | assert.deepEqual(t, {
37 | 2: { a: { b: { c: 2 } } }
38 | , 3: { a: { b: { c: 3 } } }
39 | , 4: { a: { b: { c: 4 } } }
40 | });
41 | });
42 |
43 | it('should handle nested values 1', function () {
44 | var o = [ { a: { b: { c: 2, d: 'A' } } }, { a: { b: { c: 3, d: 'B' } } }, { a: { b: { c: 4, d: 'C' } } } ]
45 | , t = trans(o).object('a.b.c', 'a.b.d').value();
46 | assert.deepEqual(t, {
47 | 2: 'A'
48 | , 3: 'B'
49 | , 4: 'C'
50 | });
51 | });
52 |
53 | it('should handle nested values 2', function () {
54 | var o = [
55 | { a: { b: { c: 1 } }, d: { e: 'A' } }
56 | , { a: { b: { c: 2 } }, d: { e: 'B' } }
57 | , { a: { b: { c: 3 } }, d: { e: 'C' } }
58 | , { a: { b: { c: 4 } }, d: { e: 'D' } } ]
59 | , t = trans(o).object('a.b.c', 'd.e').value();
60 | assert.deepEqual(t, {
61 | 1: 'A'
62 | , 2: 'B'
63 | , 3: 'C'
64 | , 4: 'D'
65 | });
66 | });
67 |
68 | it('should allow any key and any value on each source object', function () {
69 | var o = [
70 | { a: { b: 'A' }, c: [ { d: 10 }, { d: 20 }, { d: 30 } ] }
71 | , { a: { b: 'B' }, c: [ { d: 11 }, { d: 21 }, { d: 31 } ] }
72 | , { a: { b: 'C' }, c: [ { d: 12 }, { d: 22 }, { d: 32 } ] } ]
73 | , t = trans(o).object('a.b', 'c.d', ['concat', '_FOO']).value();
74 | assert.deepEqual(t, {
75 | A_FOO: [ 10, 20, 30 ]
76 | , B_FOO: [ 11, 21, 31 ]
77 | , C_FOO: [ 12, 22, 32 ]
78 | });
79 | });
80 |
81 | it('should allow keys that are arrays', function () {
82 | var o = [
83 | { a: [ 8, 2, 3 ], b: '823' }
84 | , { a: [ 2, 3, 5 ], b: '235' }
85 | , { a: [ 2, 3, 5, 9 ], b: '2359' } ]
86 | , t = trans(o).object('a', 'b').value();
87 | assert.deepEqual(t, { '8,2,3': '823' , '2,3,5': '235' , '2,3,5,9': '2359' });
88 | });
89 |
90 | it('should allow keys that are arrays and apply transformations', function () {
91 | var o = [
92 | { a: [ 8, 2, 3 ], b: '823' }
93 | , { a: [ 2, 3, 5 ], b: '235' }
94 | , { a: [ 2, 3, 5, 9 ], b: '2359' } ]
95 | , t = trans(o).object('a', 'b', sum).value();
96 | assert.deepEqual(t, { 13: '823' , 10: '235' , 19: '2359' });
97 | });
98 |
99 | it('should allow keys that are items of an array', function () {
100 | var o = [
101 | { a: [ 8, 2, 4 ], b: '824' }
102 | , { a: [ 1, 3, 5 ], b: '135' }
103 | , { a: [ 7, 6 ], b: '76' } ]
104 | , t = trans(o).object('a.', 'b').value();
105 | assert.deepEqual(t, {
106 | 1: '135'
107 | , 2: '824'
108 | , 3: '135'
109 | , 4: '824'
110 | , 5: '135'
111 | , 6: '76'
112 | , 7: '76'
113 | , 8: '824'
114 | });
115 | });
116 |
117 | it('should let the last value win in case of collisions', function () {
118 | var o = [ { a: 1, b: 'A' }, { a: 2, b: 'B' }, { a: 1, b: 'C' } ]
119 | , t = trans(o).object('a', 'b').value();
120 | assert.deepEqual(t, { 1: 'C', 2: 'B' });
121 | });
122 |
123 | it('should make the array index available to the key field transformer', function () {
124 | var o = [ { a: 1, b: 'A' }, { a: 2, b: 'B' }, { a: 1, b: 'C' } ]
125 | , t = trans(o)
126 | .object('a', 'b', function (x) { return x + ':' + this.getIndex(); })
127 | .value();
128 | assert.deepEqual(t, { '1:0': 'A', '2:1': 'B', '1:2': 'C' });
129 | });
130 |
131 | it('should apply key transformations before creating the object', function () {
132 | var o = [ { a: 'abc', b: 1 }, { a: 'cde', b: 2 }, { a: 'efg', b: 3 } ]
133 | , t = trans(o).object('a', null, 'toUpperCase').value();
134 | assert.deepEqual(t, {
135 | ABC: { a: 'abc', b: 1 }
136 | , CDE: { a: 'cde', b: 2 }
137 | , EFG: { a: 'efg', b: 3 }
138 | });
139 | });
140 |
141 | it('should work with nested arrays 1', function () {
142 | var o = [
143 | { a: [ { b: 1 }, { b: 2 } ] }
144 | , { a: [ { b: 2 }, { b: 2 } ] }
145 | , { a: [ { b: 3 }, { b: 2 } ] } ]
146 | , t = trans(o).object('a.b', 'a', sum).value();
147 | assert.deepEqual(t, {
148 | 3: [ { b: 1 }, { b: 2 } ]
149 | , 4: [ { b: 2 }, { b: 2 } ]
150 | , 5: [ { b: 3 }, { b: 2 } ]
151 | });
152 | });
153 |
154 | it('should work with nested arrays 2', function () {
155 | var o = [
156 | { a: [ { b: 1 }, { b: 2 } ] }
157 | , { a: [ { b: 3 }, { b: 4 } ] }
158 | , { a: [ { b: 5 }, { b: 6 } ] } ]
159 | , t = trans(o)
160 | .object('a.b.', 'a.b', function (x) { return x + ':' + this.getIndexes(); })
161 | .value();
162 | assert.deepEqual(t, {
163 | '1:0,0': [ 1, 2 ]
164 | , '2:1,0': [ 1, 2 ]
165 | , '3:0,1': [ 3, 4 ]
166 | , '4:1,1': [ 3, 4 ]
167 | , '5:0,2': [ 5, 6 ]
168 | , '6:1,2': [ 5, 6 ]
169 | });
170 | });
171 |
172 | it('should objectify an array of strings', function () {
173 | var o = [ 'abc', 'def', 'wow', 'dew', 'pow', 'wow', 'dew' ]
174 | , t = trans(o).object().value();
175 | assert.deepEqual(t, { abc: 'abc', def: 'def', wow: 'wow', dew: 'dew', pow: 'pow' });
176 | });
177 |
178 | it('should objectify an array of numbers', function () {
179 | var o = [ 1, 2, 1, 1, 3, 0, 0 ]
180 | , t = trans(o).object(null, null, [add, 1]).value();
181 | assert.deepEqual(t, { 2: 1, 3: 2, 4: 3, 1: 0 });
182 | });
183 |
184 | it('should objectify an array of objects', function () {
185 | var o = [ 1, 2, 1, 1, 3, 0, 0 ]
186 | , t = trans(o)
187 | .map('.', function(x) { return { k: x, v: Boolean(x) }; })
188 | .object('k', 'v')
189 | .value();
190 | assert.deepEqual(t, { 1: true, 2: true, 3: true, 0: false });
191 | });
192 |
193 | it('should handle missing values', function () {
194 | var o = [ { a: { b: 1 } }, { a: {} }, { a: { b: 2 } } ]
195 | , t = trans(o).object('a.b', 'a').value();
196 | assert.deepEqual(t, { 1: { b: 1 }, null: {}, 2: { b: 2 } });
197 | });
198 |
199 | it('should throw if the target is not an array', function () {
200 | assert.throws(function () {
201 | trans({ a: 1 }).object('a');
202 | }, /the object target is not an array/i);
203 | });
204 | });
205 |
206 | describe('objectf', function () {
207 | it('should objectify the array at the given field', function () {
208 | var o = { a: { b: [ { c: 1, d: 'one' }, { c: 2, d: 'two' } ] } }
209 | , t = trans(o).objectf('a.b', 'c', 'd', square).value();
210 | assert.deepEqual(t, { a: { b: { 1: 'one', 4: 'two' } } });
211 | });
212 |
213 | it('should objectify all target arrays 1', function () {
214 | var o = [ { a: [ 1, 2 ] }, { a: [ 2, 4 ] }, { a: [ 5 ] } ]
215 | , t = trans(o).objectf('a').value();
216 | assert.deepEqual(t, [
217 | { a: { 1: 1, 2: 2 } }
218 | , { a: { 2: 2, 4: 4 } }
219 | , { a: { 5: 5 } }
220 | ]);
221 | });
222 |
223 | it('should objectify all target arrays 2', function () {
224 | var o = [ { a: [ 'ab', 'cd' ] }, { a: [ 'foo', 'bar' ] }, { a: [ 'aaa' ] } ]
225 | , t = trans(o).objectf('a', null, null, ['charAt', 0], 'toUpperCase').value();
226 | assert.deepEqual(t, [
227 | { a: { A: 'ab', C: 'cd' } }
228 | , { a: { F: 'foo', B: 'bar' } }
229 | , { a: { A: 'aaa' } }
230 | ]);
231 | });
232 | });
233 |
234 | describe('objectff', function () {
235 | it('should objectify the source array and set it on the destination', function () {
236 | var o = { a: { b: [ { c: 1, d: 'one' }, { c: 2, d: 'two' } ] }, e: 'ready' }
237 | , t = trans(o).objectff('a.b', 'e', 'c', 'd').value();
238 | assert.deepEqual(t, {
239 | a: { b: [ { c: 1, d: 'one' }, { c: 2, d: 'two' } ] }
240 | , e: { 1: 'one', 2: 'two' }
241 | });
242 | });
243 | });
244 | };
245 |
--------------------------------------------------------------------------------
/test/trans/sort-test.js:
--------------------------------------------------------------------------------
1 | module.exports = function (util) {
2 | var trans = util.trans
3 | , assert = util.assert
4 | , square = util.square
5 | , mod = util.mod
6 | , sum = util.sum
7 | , add = util.add;
8 |
9 | describe('sort', function () {
10 | it('should sort an array of numbers', function () {
11 | var o = [ 1, 1, 3, 4, 1, 1 ]
12 | , t = trans(o).sort().value();
13 | assert.deepEqual(t, [ 1, 1, 1, 1, 3, 4 ]);
14 | });
15 |
16 | it('should sort an array of numbers in descending order', function () {
17 | var o = [ 1, 1, 3, 4, 1, 1 ]
18 | , t = trans(o).sort(':desc').value();
19 | assert.deepEqual(t, [ 4, 3, 1, 1, 1, 1 ]);
20 | });
21 |
22 | it('should sort an array of strings', function () {
23 | var o = [ 'Ash', 'bar', 'Baz', 'baz', 'bak', 'Foo', 'ash' ]
24 | , t = trans(o).sort(null, 'toLowerCase').value();
25 | assert.deepEqual(t, [ 'Ash', 'ash', 'bak', 'bar', 'Baz', 'baz', 'Foo' ]);
26 | });
27 |
28 | it('should allow specifying fields as part of the transformers', function () {
29 | var o = [
30 | { a: { b: [ 1, 2 ] } }
31 | , { a: { b: [ 1 ] } }
32 | , { a: { b: [ 2, 2, 1 ] } }
33 | , { a: { b: [] } } ]
34 | , t = trans(o).sort('a', 'b', sum, [mod, 4]).value();
35 | assert.deepEqual(t, [
36 | { a: { b: [] } }
37 | , { a: { b: [ 1 ] } }
38 | , { a: { b: [ 2, 2, 1 ] } }
39 | , { a: { b: [ 1, 2 ] } }
40 | ]);
41 | });
42 |
43 | it('should allow specifying the sort field with dot notation', function () {
44 | var o = [
45 | { a: { b: [ 1, 2 ] } }
46 | , { a: { b: [ 1 ] } }
47 | , { a: { b: [ 2, 2, 2 ] } }
48 | , { a: { b: [] } } ]
49 | , t = trans(o).sort('a.b', 'length').value();
50 | assert.deepEqual(t, [
51 | { a: { b: [] } }
52 | , { a: { b: [ 1 ] } }
53 | , { a: { b: [ 1, 2 ] } }
54 | , { a: { b: [ 2, 2, 2 ] } }
55 | ]);
56 | });
57 |
58 | it('should work with nested arrays 1', function () {
59 | var o = [
60 | { a: { b: [ { c: 1 }, { c: 2 } ] } }
61 | , { a: { b: [ { c: 2 }, { c: 2 } ] } }
62 | , { a: { b: [ { c: 2 }, { c: 1 } ] } }
63 | , { a: { b: [ { c: 3 }, { c: 1 } ] } }
64 | , { a: { b: [ { c: 1 }, { c: 1 } ] } }
65 | ]
66 | , e = [
67 | { a: { b: [ { c: 1 }, { c: 1 } ] } }
68 | , { a: { b: [ { c: 1 }, { c: 2 } ] } }
69 | , { a: { b: [ { c: 2 }, { c: 1 } ] } }
70 | , { a: { b: [ { c: 2 }, { c: 2 } ] } }
71 | , { a: { b: [ { c: 3 }, { c: 1 } ] } }
72 | ]
73 | , t = trans(o).sort('a.b.c', sum).value();
74 | assert.deepEqual(t, e);
75 | });
76 |
77 | it('should work with nested arrays 2', function () {
78 | var o = [
79 | [ { a: 1 }, { a: 2 } ]
80 | , [ { a: 5 } ]
81 | , [ { a: 0 }, { a: 1 }, { a: 1 } ]
82 | , [ { a: 2 }, { a: 2 } ] ]
83 | , t = trans(o).sort('a', sum).value();
84 | assert.deepEqual(t, [
85 | [ { a: 0 }, { a: 1 }, { a: 1 } ]
86 | , [ { a: 1 }, { a: 2 } ]
87 | , [ { a: 2 }, { a: 2 } ]
88 | , [ { a: 5 } ]
89 | ]);
90 | });
91 |
92 | it('should sort with a specified comparer', function () {
93 | var o = [ { a: 1, b: 1 }, { a: 2, b: 1 }, { a: 1, b: 2 }, { a: 1, b: 0 } ]
94 | , t = trans(o).sort(null, function (x, y) {
95 | return x.a === y.a ? x.b - y.b : x.a - y.a;
96 | }).value();
97 | assert.deepEqual(t, [
98 | { a: 1, b: 0 }
99 | , { a: 1, b: 1 }
100 | , { a: 1, b: 2 }
101 | , { a: 2, b: 1 }
102 | ]);
103 | });
104 |
105 | it('should sort by a given field with a specified comparer', function () {
106 | var o = [
107 | { a: 'z', b: 1 }
108 | , { a: 'a', b: 2 }
109 | , { a: 'aa', b: 3 }
110 | , { a: 'zaa', b: 4 }
111 | , { a: 'ba', b: 5 }
112 | , { a: 'ca', b: 6 }
113 | , { a: 'ccc', b: 7 } ]
114 | , t = trans(o).sort('a', function (x, y) {
115 | return x.length === y.length ? x.localeCompare(y) : x.length - y.length;
116 | }).value();
117 | assert.deepEqual(t, [
118 | { a: 'a', b: 2 }
119 | , { a: 'z', b: 1 }
120 | , { a: 'aa', b: 3 }
121 | , { a: 'ba', b: 5 }
122 | , { a: 'ca', b: 6 }
123 | , { a: 'ccc', b: 7 }
124 | , { a: 'zaa', b: 4 }
125 | ]);
126 | });
127 |
128 | it('should work with missing fields 1', function () {
129 | var o = [ { a: { b: 1 } }, { a: { b: 5 } }, { a: {} }, { a: { b: 2 } }, { a: {} } ]
130 | , t = trans(o).sort('a.b').value();
131 | assert.deepEqual(t, [
132 | { a: {} }, { a: {} }, { a: { b: 1 } }, { a: { b: 2 } }, { a: { b: 5 } }
133 | ]);
134 | });
135 |
136 | it('should work with missing fields 2', function () {
137 | var o = [ { a: { b: 0 } }, { a: { b: 5 } }, { a: {} }, { a: { b: 2 } }, { a: {} } ]
138 | , t = trans(o).sort('a.b', [add, 1]).value();
139 | assert.deepEqual(t, [
140 | { a: { b: 0 } }, { a: {} }, { a: {} }, { a: { b: 2 } }, { a: { b: 5 } }
141 | ]);
142 | });
143 |
144 | it('should use a stable sort 1', function () {
145 | var o = [ { a: 1, b: 1 }, { a: 0, b: 2 }, { a: 0, b: 3 }, { a: 1, b: 4 }, { a: 2, b: 5 }, { a: 3, b: 6 } ]
146 | , t = trans(o).sort('a').value();
147 | assert.deepEqual(t, [
148 | { a: 0, b: 2 }
149 | , { a: 0, b: 3 }
150 | , { a: 1, b: 1 }
151 | , { a: 1, b: 4 }
152 | , { a: 2, b: 5 }
153 | , { a: 3, b: 6 }
154 | ]);
155 | });
156 |
157 | it('should use a stable sort 2', function () {
158 | var o = [ { a: 1, b: 1 }, { a: 0, b: 2 }, { a: 0, b: 3 }, { a: 1, b: 4 }, { a: 2, b: 5 }, { a: 3, b: 6 } ]
159 | , t = trans(o).sort('a:descending').value();
160 | assert.deepEqual(t, [
161 | { a: 3, b: 6 }
162 | , { a: 2, b: 5 }
163 | , { a: 1, b: 1 }
164 | , { a: 1, b: 4 }
165 | , { a: 0, b: 2 }
166 | , { a: 0, b: 3 }
167 | ]);
168 | });
169 |
170 | it('should throw if the sort target is not an array', function () {
171 | assert.throws(function () {
172 | trans({ a: 1 }).sort('a');
173 | }, /the sort target is not an array/i);
174 | });
175 | });
176 |
177 | describe('sortf', function () {
178 | it('should sort the target array', function () {
179 | var o = {
180 | a: { b: 1 }
181 | , c: { d: [ 1, 1, 5, 3, 3, 4, 0, ] }
182 | }
183 | , t = trans(o).sortf('c.d', null, [mod, 3]).value();
184 | assert.deepEqual(t, {
185 | a: { b: 1 }
186 | , c: { d: [ 3, 3, 0, 1, 1, 4, 5, ] }
187 | });
188 | });
189 |
190 | it('should sort the target array by the given field', function () {
191 | var o = { a: { b: [ { c: 1 }, { c: 7 }, { c: 3 }, { c: 2 } ] } }
192 | , t = trans(o).sortf('a.b', 'c').value();
193 | assert.deepEqual(t, {
194 | a: { b: [ { c: 1 }, { c: 2 }, { c: 3 }, { c: 7 } ] }
195 | });
196 | });
197 |
198 | it('should sort the target array by the given field and transformer', function () {
199 | var o = { a: { b: [ { c: [ 1 ] }, { c: [] }, { c: [ 3, 4, 5 ] }, { c: [ 2 ] } ] } }
200 | , t = trans(o).sortf('a.b', 'c', 'length').value();
201 | assert.deepEqual(t, {
202 | a: { b: [ { c: [] }, { c: [ 1 ] }, { c: [ 2 ] }, { c: [ 3, 4, 5 ] } ] }
203 | });
204 | });
205 |
206 | it('should sort all targets', function () {
207 | var o = [ { a: [ 1, 4, 2, 1 ] }, { a: [ 2, 2, 1, 4 ] }, { a: [ 3, 1, 2 ] } ]
208 | , t = trans(o).sortf('a').value();
209 | assert.deepEqual(t, [
210 | { a: [ 1, 1, 2, 4 ] }, { a: [ 1, 2, 2, 4 ] }, { a: [ 1, 2, 3 ] }
211 | ]);
212 | });
213 | });
214 |
215 | describe('sortff', function () {
216 | it('should sort the target array and set it on the destination field', function () {
217 | var o = { a: [ 1, 2, 1, 1, 3, 2 ] }
218 | , t = trans(o).sortff('a', 'c').value();
219 | assert.deepEqual(t, { a: [ 1, 2, 1, 1, 3, 2 ], c: [ 1, 1, 1, 2, 2, 3 ] });
220 | });
221 |
222 | it('should sort multiple times', function () {
223 | var o = [
224 | { a: { b: [ { c: 5 }, { c: 2 }, { c: 3 }, { c: 4 } ] } }
225 | , { a: {
226 | b: [ { c: 199 }, { c: 290 }, { c: 112 } ]
227 | , d: [ { c: 'five' }, { c: 'two' }, { c: 'three' }, { c: 'four' } ]
228 | } }
229 | ]
230 | , t = trans(o)
231 | .sortff('a.b', 'a.numeric', 'c')
232 | .sortff('a.d', 'a.alpha', 'c')
233 | .sortff('a.d', 'a.size', 'c', 'length')
234 | .value();
235 | assert.deepEqual(t, [
236 | { a: {
237 | b : [ { c: 5 }, { c: 2 }, { c: 3 }, { c: 4 } ]
238 | , numeric : [ { c: 2 }, { c: 3 }, { c: 4 }, { c: 5 } ]
239 | , alpha : null
240 | , size : null
241 | } }
242 | , { a: {
243 | b : [ { c: 199 }, { c: 290 }, { c: 112 } ]
244 | , d : [ { c: 'five' }, { c: 'two' }, { c: 'three' }, { c: 'four' } ]
245 | , numeric : [ { c: 112 }, { c: 199 }, { c: 290 } ]
246 | , alpha : [ { c: 'five' }, { c: 'four' }, { c: 'three' }, { c: 'two' } ]
247 | , size : [ { c: 'two' }, { c: 'five' }, { c: 'four' }, { c: 'three' } ]
248 | } }
249 | ]);
250 | });
251 | });
252 | };
253 |
--------------------------------------------------------------------------------
/test/trans/omit-test.js:
--------------------------------------------------------------------------------
1 | module.exports = function (util) {
2 | var trans = util.trans
3 | , assert = util.assert
4 | , square = util.square
5 | , mod = util.mod
6 | , sum = util.sum
7 | , add = util.add;
8 |
9 | describe('omit', function () {
10 | it('should remove the specified fields', function () {
11 | var o = {
12 | a: {
13 | b: 1
14 | , c: 2
15 | , p: { d: 3, e: 4 }
16 | , q: { g: 5, h: 6, f: { k: 8, l: 10, r: 22 } }
17 | }
18 | , m: { n: 11 }
19 | }
20 | , t = trans(o).omit('a.b', 'a.p.e', 'a.q.g', 'a.q.f.r', 'm').value();
21 | assert.deepEqual(t, {
22 | a: {
23 | c: 2
24 | , p: { d: 3 }
25 | , q: { h: 6, f: { k: 8, l: 10 } }
26 | }
27 | });
28 | });
29 |
30 | it('should not remove null fields if not specified', function () {
31 | var o = { a: 1, b: { c: 'a', d: null, e: 2 } }
32 | , t = trans(o).omit('b.c').value();
33 | assert.deepEqual(t, { a: 1, b: { d: null, e: 2 } });
34 | });
35 |
36 | it('should create a deep copy if no fields are specified', function () {
37 | var o = {
38 | a: {
39 | b: 1
40 | , c: 2
41 | , p: { d: 3, e: 4 }
42 | , q: { g: 5, h: 6, f: { k: 8, l: 10, r: 22 } }
43 | , r: [ { s: { t: [ 1 ] } }, { s: { t: [ 2, 3, 4 ] } } ]
44 | }
45 | , m: { n: 11 }
46 | }
47 | , t = trans(o).omit().value();
48 | assert.deepEqual(t, o);
49 |
50 | delete t.a.r[0].s;
51 | delete t.a.p.q;
52 | t.a.r[1].s.t.push(100);
53 |
54 | assert.deepEqual(o, {
55 | a: {
56 | b: 1
57 | , c: 2
58 | , p: { d: 3, e: 4 }
59 | , q: { g: 5, h: 6, f: { k: 8, l: 10, r: 22 } }
60 | , r: [ { s: { t: [ 1 ] } }, { s: { t: [ 2, 3, 4 ] } } ]
61 | }
62 | , m: { n: 11 }
63 | });
64 | });
65 |
66 | it('should handle circular references', function () {
67 | var o = { a: { b: [ { c: 1, d: 2 }, { c: 3, d: 4 } ] } }
68 | , t = null;
69 |
70 | o = trans(o).mapf('a.b.c', function () { return o; }).value();
71 | o.e = o;
72 | t = trans(o).omit().value();
73 | });
74 |
75 | it('should not modify the original object', function () {
76 | var o = {
77 | a: {
78 | b: 1
79 | , c: 2
80 | , p: { d: 3, e: 4 }
81 | , q: { g: 5, h: 6, f: { k: 8, l: 10, r: 22 } }
82 | , r: [ { s: { t: [ 1 ] } }, { s: { t: [ 2, 3, 4 ] } } ]
83 | }
84 | , m: { n: 11 }
85 | }
86 | , t = trans(o).omit('a.b', 'a.p.d', 'a.r.s.t').value();
87 | assert.deepEqual(o, {
88 | a: {
89 | b: 1
90 | , c: 2
91 | , p: { d: 3, e: 4 }
92 | , q: { g: 5, h: 6, f: { k: 8, l: 10, r: 22 } }
93 | , r: [ { s: { t: [ 1 ] } }, { s: { t: [ 2, 3, 4 ] } } ]
94 | }
95 | , m: { n: 11 }
96 | });
97 | });
98 |
99 | it('should remove the specified fields on all objects in an array', function () {
100 | var o = [
101 | { a: { b: 'a', c: 'b' }, d: { e: 1, f: 2 } }
102 | , { a: { b: 'b', c: 'e' }, d: { e: 2, f: 3 } }
103 | , { a: { b: 'c', c: 'f' }, d: { e: 3, f: 4 } }
104 | , { a: { b: 'd', c: 'g' }, d: { e: 4, f: 5 } }
105 | ]
106 | , t = trans(o).omit('a', 'd.f').value();
107 | assert.deepEqual(t, [
108 | { d: { e: 1 } }
109 | , { d: { e: 2 } }
110 | , { d: { e: 3 } }
111 | , { d: { e: 4 } }
112 | ]);
113 | });
114 |
115 | it('should handle missing keys', function () {
116 | var o = [ { a: { b: 1, c: 2 } }, { a: { c: 3 } }, {} ]
117 | , t = trans(o).omit('a.c').value();
118 | assert.deepEqual(t, [ { a: { b: 1 } }, { a: {} }, {} ]);
119 | });
120 |
121 | it('should handle functions', function () {
122 | var o = { a: { b: 1, c: function () { return 1; } }, e: 2, f: 3 }
123 | , t = trans(o).omit('a.b', 'e').value();
124 | assert.strictEqual(
125 | util.stringify(t)
126 | , util.stringify({ a: { c: function () { return 1; } }, f: 3 }));
127 | });
128 |
129 | it('should work with nested arrays 1', function () {
130 | var o = [
131 | { a: { b: [ { c: 10, d: 10 }, { c: 20, d: 20 } ], d: 'a', e: [ { f: 2 } ] } }
132 | , { a: { b: [ { c: 11, d: 11 }, { c: 12, d: 12 } ], d: 'b', e: [ { f: 2 } ] } }
133 | , { a: { b: [ { c: 21, d: 21 }, { c: 22, d: 22 } ], d: 'c', e: [ { f: 2 } ] } }
134 | ]
135 | , t = trans(o).omit('a.b.c', 'a.e').value();
136 | assert.deepEqual(t, [
137 | { a: { b: [ { d: 10 }, { d: 20 } ], d: 'a' } }
138 | , { a: { b: [ { d: 11 }, { d: 12 } ], d: 'b' } }
139 | , { a: { b: [ { d: 21 }, { d: 22 } ], d: 'c' } }
140 | ]);
141 | });
142 |
143 | it('should handle primitives', function () {
144 | var o = { a: { b: 1 } }
145 | , t = trans(o).omit('a.b.c').value();
146 | assert.deepEqual(t, o);
147 | });
148 |
149 | it('should work with nested arrays 2', function () {
150 | var o = [ [
151 | { a: [
152 | { b: 1, c: [
153 | { d: 1, e: 2, f: { g: 1, h: 2 } }, { d: 1, e: 2, f: { g: 1, h: 2 } } ]
154 | }
155 | , { b: 2, c: [
156 | { d: 2, e: 3, f: { g: 2, h: 3 } }, { d: 2, e: 3, f: { g: 2, h: 3 } } ]
157 | }
158 | , { b: 2, c: [
159 | { d: 2, e: 3, f: { g: 2, h: 3 } }, { d: 2, e: 3, f: { g: 2, h: 3 } } ]
160 | } ]
161 | , b: 4
162 | }
163 | , { a: [
164 | { b: 1, c: [
165 | { d: 1, e: 2, f: { g: 1, h: 2 } }, { d: 1, e: 2, f: { g: 1, h: 2 } } ]
166 | }
167 | , { b: 2, c: [
168 | { d: 2, e: 3, f: { g: 2, h: 3 } }, { d: 2, e: 3, f: { g: 2, h: 3 } } ]
169 | }
170 | , { b: 2, c: [
171 | { d: 2, e: 3, f: { g: 2, h: 3 } }, { d: 2, e: 3, f: { g: 2, h: 3 } } ]
172 | } ]
173 | , b: 4
174 | }]
175 | , [
176 | { a: [
177 | { b: 1, c: [
178 | { d: 1, e: 2, f: { g: 1, h: 2 } }, { d: 1, e: 2, f: { g: 1, h: 2 } } ]
179 | }
180 | , { b: 2, c: [
181 | { d: 2, e: 3, f: { g: 2, h: 3 } }, { d: 2, e: 3, f: { g: 2, h: 3 } } ]
182 | }
183 | , { b: 2, c: [
184 | { d: 2, e: 3, f: { g: 2, h: 3 } }, { d: 2, e: 3, f: { g: 2, h: 3 } } ]
185 | } ]
186 | , b: 4
187 | }
188 | , { a: [
189 | { b: 1, c: [
190 | { d: 1, e: 2, f: { g: 1, h: 2 } }, { d: 1, e: 2, f: { g: 1, h: 2 } } ]
191 | }
192 | , { b: 2, c: [
193 | { d: 2, e: 3, f: { g: 2, h: 3 } }, { d: 2, e: 3, f: { g: 2, h: 3 } } ]
194 | }
195 | , { b: 2, c: [
196 | { d: 2, e: 3, f: { g: 2, h: 3 } }, { d: 2, e: 3, f: { g: 2, h: 3 } } ]
197 | } ]
198 | , b: 4
199 | }]]
200 | , t = trans(o).omit('b', 'a.b', 'a.c.d', 'a.c.f.g').value();
201 | assert.deepEqual(t, [
202 | [
203 | { a: [
204 | { c: [ { e: 2, f: { h: 2 } }, { e: 2, f: { h: 2 } } ] }
205 | , { c: [ { e: 3, f: { h: 3 } }, { e: 3, f: { h: 3 } } ] }
206 | , { c: [ { e: 3, f: { h: 3 } }, { e: 3, f: { h: 3 } } ] } ]
207 | }
208 | , { a: [
209 | { c: [ { e: 2, f: { h: 2 } }, { e: 2, f: { h: 2 } } ] }
210 | , { c: [ { e: 3, f: { h: 3 } }, { e: 3, f: { h: 3 } } ] }
211 | , { c: [ { e: 3, f: { h: 3 } }, { e: 3, f: { h: 3 } } ] } ]
212 | }
213 | ]
214 | , [
215 | { a: [
216 | { c: [ { e: 2, f: { h: 2 } }, { e: 2, f: { h: 2 } } ] }
217 | , { c: [ { e: 3, f: { h: 3 } }, { e: 3, f: { h: 3 } } ] }
218 | , { c: [ { e: 3, f: { h: 3 } }, { e: 3, f: { h: 3 } } ] } ]
219 | }
220 | , { a: [
221 | { c: [ { e: 2, f: { h: 2 } }, { e: 2, f: { h: 2 } } ] }
222 | , { c: [ { e: 3, f: { h: 3 } }, { e: 3, f: { h: 3 } } ] }
223 | , { c: [ { e: 3, f: { h: 3 } }, { e: 3, f: { h: 3 } } ] } ]
224 | }
225 | ] ]);
226 | });
227 |
228 | it('should clone dates', function () {
229 | var date1 = new Date()
230 | , date2 = new Date()
231 | , o = { a: { b: 1, c: date1 }, e: 2, f: date2 }
232 | , t = trans(o).omit('a.b', 'e').value();
233 | assert.strictEqual(
234 | util.stringify(t)
235 | , util.stringify({ a: { c: date1 }, f: date2 }));
236 | });
237 |
238 | });
239 |
240 | describe('omitf', function () {
241 | it('should apply omit at the given field 1', function () {
242 | var o = { a: 1, b: { c: [ { d: 1, e: 2 }, { d: 3, e: 4 } ], f: 'b' }, g: 'c' }
243 | , t = trans(o).omitf('b.c', 'e').value();
244 | assert.deepEqual(t, { a: 1, b: { c: [ { d: 1 }, { d: 3 } ], f: 'b' }, g: 'c' });
245 | });
246 |
247 | it('should apply omit at the given field 2', function () {
248 | var o = { a: 1, b: { c: [ { d: 1, e: 2 }, { d: 3, e: 4 } ], f: 'b' }, g: 'c' }
249 | , t = trans(o).omitf('b', 'c.e', 'f').value();
250 | assert.deepEqual(t, { a: 1, b: { c: [ { d: 1 }, { d: 3 } ] }, g: 'c' });
251 | });
252 | });
253 |
254 | describe('omitff', function () {
255 | it('should apply omit on the target and set it on the destination', function () {
256 | var o = { a: 1, b: { c: [ { d: 1, e: 2 }, { d: 3, e: 4 } ], f: 'b' }, g: 'c' }
257 | , t = trans(o).omitff('b.c', 'b.p', 'e').value();
258 | assert.deepEqual(t, {
259 | a: 1
260 | , b: {
261 | c: [ { d: 1, e: 2 }, { d: 3, e: 4 } ]
262 | , p: [ { d: 1 }, { d: 3 } ]
263 | , f: 'b'
264 | }
265 | , g: 'c'
266 | });
267 | });
268 | });
269 | };
270 |
--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
1 | var slice = Array.prototype.slice
2 | , concat = Array.prototype.concat
3 | , all = Array.prototype.every
4 | , any = Array.prototype.some
5 | , Context = require('./context').Context
6 | , Trav = require('./trav').Trav
7 | , util = require('./util')
8 | , opath = require('./opath')
9 | , ocopy = require('./ocopy')
10 | , osort = require('./osort')
11 | , ensure = util.ensure
12 | , fail = util.fail
13 | , isArray = util.isArray
14 | , isFunction = util.isFunction
15 | , isUndefined = util.isUndefined
16 | , isObject = util.isObject
17 | , isString = util.isString
18 | , toArray = util.toArray;
19 |
20 | function Trans (obj, options, context) {
21 | this._state_ = obj;
22 | this._context_ = new Context(context);
23 | this._trav_ = new Trav(this._context_);
24 | }
25 |
26 | function flatten (list, shallow) {
27 | if (!isArray(list)) { return list; }
28 | if (shallow && all.call(list, isArray)) { return concat.apply([], list); }
29 |
30 | var i = 0
31 | , len = list.length
32 | , out = []
33 | , item = null;
34 |
35 | for (i = 0; i < len; i++) {
36 | item = list[i];
37 | if (!isArray(item)) {
38 | out.push(item);
39 | } else if (shallow) {
40 | out = out.concat(item);
41 | } else {
42 | out = out.concat(flatten(item, false));
43 | }
44 | }
45 |
46 | return out;
47 | }
48 |
49 | function objToArray (obj, k, v) {
50 | return Object.keys(obj).map(function (key) {
51 | var pair = {};
52 | pair[k] = key;
53 | pair[v] = obj[key];
54 | return pair;
55 | });
56 | }
57 |
58 | module.exports = function (obj) {
59 | return new Trans(obj);
60 | };
61 |
62 | Trans.prototype.value = function() {
63 | return this._state_;
64 | };
65 |
66 | Trans.prototype.count = function () {
67 | return isArray(this._state_) ? this._state_.length : 1;
68 | };
69 |
70 | Trans.prototype.get = function(cb) {
71 | if (isFunction(cb)) {
72 | cb(this._state_);
73 | }
74 | return this;
75 | };
76 |
77 | Trans.prototype._create_ = function(obj) {
78 | return new Trans(obj, null, this._context_);
79 | };
80 |
81 | Trans.prototype.skip = function(n) {
82 | return this.map(['slice', n]);
83 | };
84 |
85 | Trans.prototype.take = function(n) {
86 | return this.map(['slice', 0, n]);
87 | };
88 |
89 | Trans.prototype.first = function() {
90 | return this.map(['slice', 0, 1], 'shift', [util.default, null]);
91 | };
92 |
93 | Trans.prototype.last = function() {
94 | return this.map(['slice', -1], 'pop', [util.default, null]);
95 | };
96 |
97 | Trans.prototype.uniq = function() {
98 | var seen = {}
99 | , args = toArray(arguments);
100 |
101 | if (args.length === 0) {
102 | args.push(null);
103 | }
104 |
105 | args.push(function (value) {
106 | var key = value + typeof value;
107 |
108 | if (value === null || isUndefined(value)) {
109 | return true;
110 | } else if (seen[key]) {
111 | return false;
112 | } else {
113 | seen[key] = true;
114 | return true;
115 | }
116 | });
117 |
118 | return this.filter.apply(this, args);
119 | };
120 |
121 | Trans.prototype.copy = function() {
122 | return this.omit();
123 | };
124 |
125 | Trans.prototype.remove = function() {
126 | var fields = toArray(arguments);
127 | if (fields.length > 0) {
128 | ocopy.removeFields(this._state_, fields);
129 | }
130 | return this;
131 | };
132 |
133 | Trans.prototype.omit = function() {
134 | this._state_ = ocopy.copyBlack(this._state_, toArray(arguments));
135 | return this;
136 | };
137 |
138 | Trans.prototype.pick = function() {
139 | var fields = toArray(arguments);
140 | if (fields.length > 0) {
141 | this._state_ = ocopy.copyWhite(this._state_, fields);
142 | }
143 | return this;
144 | };
145 |
146 | Trans.prototype.sort = function() {
147 | var args = toArray(arguments)
148 | , self = this
149 | , state = this._state_
150 | , trav = this._trav_
151 | , field = opath.parse(args[0])
152 | , desc = /desc|dsc/i.test(field.meta[0])
153 | , funs = args.slice(1)
154 | , last = funs[funs.length - 1]
155 | , comp = null;
156 |
157 | if (!state) { return this; }
158 |
159 | ensure(isArray(state), 'The sort target is not an array');
160 |
161 | if (last && isFunction(last) && last.length === 2) {
162 | comp = osort.createComparer(last);
163 | funs.pop();
164 | }
165 | if (comp === null) {
166 | comp = osort.createComparer(null, desc);
167 | }
168 |
169 | state = trav.map(state, function (val, index) {
170 | var criteria = val, t = null;
171 |
172 | if (field.exists) {
173 | criteria = trav.walkValue(val, field.path, false);
174 | }
175 | if (funs.length > 0) {
176 | t = self._create_(criteria);
177 | criteria = t.map.apply(t, funs).value();
178 | }
179 |
180 | return {
181 | index : index
182 | , value : val
183 | , criteria : criteria
184 | };
185 | });
186 |
187 | state.sort(comp);
188 |
189 | this._state_ = state.map(function (n) { return n.value; });
190 |
191 | return this;
192 | };
193 |
194 | Trans.prototype.flatten = function(deep) {
195 | return this.map([flatten, !deep]);
196 | };
197 |
198 | Trans.prototype.default = function() {
199 | var args = toArray(arguments)
200 | , key = null
201 | , val = null
202 | , i = 0
203 | , len = args.length
204 | , set = function (obj, val) {
205 | return isUndefined(obj) || obj === null ? val : obj;
206 | };
207 |
208 | ensure(len % 2 === 0, 'An even number of arguments was expected');
209 |
210 | for (i = 0; i < len - 1; i += 2) {
211 | key = args[i];
212 | val = args[i + 1];
213 | this.mapf(key, [set, val]);
214 | }
215 |
216 | return this;
217 | };
218 |
219 | Trans.prototype.array = function(keyName, valName) {
220 | var trav = this._trav_
221 | , state = this._state_
222 | , result = [];
223 |
224 | keyName = keyName || 'key';
225 | valName = valName || 'value';
226 |
227 | if (isUndefined(state) || state === null) {
228 | result = [];
229 | } else if (isArray(state)) {
230 | result = trav.walk(state, null, false, function (node) {
231 | var obj = node.parent;
232 | ensure(isObject(obj), 'Object expected but got', obj);
233 | return objToArray(obj, keyName, valName);
234 | });
235 | } else if (isObject(state)) {
236 | result = objToArray(state, keyName, valName);
237 | } else {
238 | fail('Object expected but got', state);
239 | }
240 |
241 | this._state_ = result;
242 | return this;
243 | };
244 |
245 | Trans.prototype.object = function() {
246 | var args = toArray(arguments)
247 | , state = this._state_
248 | , self = this
249 | , trav = this._trav_
250 | , keyField = opath.parse(args[0])
251 | , valField = opath.parse(args[1])
252 | , funs = args.slice(2)
253 | , res = {};
254 |
255 | var addPair = function (key, val) {
256 | var t = null;
257 |
258 | if (funs.length > 0) {
259 | t = self._create_(key);
260 | key = t.map.apply(t, funs).value();
261 | }
262 |
263 | key = isUndefined(key) ? null : key;
264 | res[key] = val;
265 | };
266 |
267 | if (!state) { return this; }
268 |
269 | ensure(isArray(state), 'The object target is not an array');
270 |
271 | trav.map(state, function (obj) {
272 | var key = obj
273 | , val = obj;
274 |
275 | if (keyField.exists) {
276 | key = trav.walkValue(obj, keyField.path, false);
277 | }
278 | if (valField.exists) {
279 | val = trav.walkValue(obj, valField.path, false);
280 | }
281 |
282 | if (keyField.iter) {
283 | trav.map(key, function (k) { addPair(k, val); });
284 | } else {
285 | addPair(key, val);
286 | }
287 | });
288 |
289 | this._state_ = res;
290 | return this;
291 | };
292 |
293 | Trans.prototype.filter = function() {
294 | var args = toArray(arguments)
295 | , self = this
296 | , trav = this._trav_
297 | , field = opath.parse(args[0])
298 | , invert = field.meta[0] === 'invert'
299 | , funs = args.slice(1);
300 |
301 | if (!this._state_) { return this; }
302 |
303 | ensure(isArray(this._state_), 'The filter target is not an array');
304 |
305 | this._state_ = trav.filter(this._state_, function (obj) {
306 | var val = obj
307 | , bval = null
308 | , t = null;
309 |
310 | if (field.exists) {
311 | val = trav.walkValue(val, field.path, false);
312 | }
313 |
314 | if (funs.length > 0) {
315 | t = self._create_(val);
316 | val = t.map.apply(t, funs).value();
317 | }
318 |
319 | bval = Boolean(val);
320 | return invert ? !bval : bval;
321 | });
322 |
323 | return this;
324 | };
325 |
326 | Trans.prototype.group = function() {
327 | var args = toArray(arguments)
328 | , self = this
329 | , trav = this._trav_
330 | , state = this._state_
331 | , keyField = opath.parse(args[0])
332 | , valField = opath.parse(args[1])
333 | , keyName = keyField.meta[0] || 'key'
334 | , valName = keyField.meta[1] || 'value'
335 | , funs = args.slice(2)
336 | , map = {}
337 | , res = [];
338 |
339 | var addPair = function (key, val, seen) {
340 | var t = null, obj = null;
341 |
342 | if (funs.length > 0) {
343 | t = self._create_(key);
344 | key = t.map.apply(t, funs).value();
345 | }
346 |
347 | key = isUndefined(key) ? null : key;
348 |
349 | if (!seen[key]) {
350 | seen[key] = true;
351 |
352 | if (!map[key]) {
353 | map[key] = [];
354 | obj = {};
355 | obj[keyName] = key;
356 | obj[valName] = map[key];
357 | res.push(obj);
358 | }
359 |
360 | map[key].push(val);
361 | }
362 | };
363 |
364 | if (!state) { return this; }
365 |
366 | ensure(isArray(state), 'The group target is not an array');
367 |
368 | trav.map(state, function (val) {
369 | var key = val
370 | , seen = {};
371 |
372 | if (keyField.exists) {
373 | key = trav.walkValue(val, keyField.path, false);
374 | }
375 | if (valField.exists) {
376 | val = trav.walkValue(val, valField.path, false);
377 | }
378 |
379 | if (keyField.iter) {
380 | trav.map(key, function (k) { addPair(k, val, seen); });
381 | } else {
382 | addPair(key, val, seen);
383 | }
384 | });
385 |
386 | this._state_ = res;
387 | return this;
388 | };
389 |
390 | Trans.prototype.pluck = function() {
391 | var args = toArray(arguments);
392 | args.splice(1, 0, null);
393 | return this.mapff.apply(this, args);
394 | };
395 |
396 | Trans.prototype.mapff = function() {
397 | var args = toArray(arguments)
398 | , self = this
399 | , funs = args.slice(2)
400 | , srcPath = opath.parse(args[0])
401 | , dstPath = opath.parse(args[1])
402 | , paths = opath.root(srcPath, dstPath)
403 | , trav = self._trav_
404 | , result = null
405 | , walk = function (obj, fn) {
406 | return trav.walk(obj, paths.root.path, false, function (node) {
407 | if (paths.dst.exists) {
408 | return trav.walk(node.getValueOrParent(), null, false, fn);
409 | } else {
410 | return fn(node);
411 | }
412 | });
413 | };
414 |
415 | if (srcPath.iter) { funs.unshift(opath.ITER); }
416 |
417 | if (paths.root.nil && paths.src.nil && paths.dst.nil) {
418 | this.map.apply(this, funs);
419 | } else {
420 | result = walk(self._state_, function (node) {
421 | var srcField = opath.join(node.field, paths.src)
422 | , dstField = opath.join(node.field, paths.dst)
423 | , t = null
424 | , val = node.getValueOrParent();
425 |
426 | if (srcField.exists) {
427 | val = trav.walk(node.parent, srcField.path, false, function (srcNode) {
428 | return srcNode.getValueOrParent();
429 | });
430 | }
431 |
432 | t = self._create_(val);
433 | val = t.map.apply(t, funs).value();
434 |
435 | if (dstField.exists) {
436 | trav.walk(node.parent, dstField.path, true, function (dstNode) {
437 | dstNode.setValue(val);
438 | });
439 | }
440 |
441 | return val;
442 | });
443 |
444 | if (dstPath.nil) {
445 | this._state_ = result;
446 | }
447 | }
448 |
449 | return this;
450 | };
451 |
452 | Trans.prototype.mapf = function() {
453 | var args = toArray(arguments)
454 | , self = this
455 | , field = opath.parse(args[0])
456 | , funs = args.slice(1);
457 |
458 | if (field.iter) {
459 | funs.unshift(opath.ITER);
460 | }
461 | if (field.nil) {
462 | return this.map.apply(this, funs);
463 | }
464 |
465 | self._trav_.walk(self._state_, field.path, true, function (node) {
466 | var t = null, val = node.getValue();
467 |
468 | t = self._create_(val);
469 | t.map.apply(t, funs);
470 | node.setValue(t.value());
471 | });
472 |
473 | return this;
474 | };
475 |
476 | Trans.prototype.map = function() {
477 | this._state_ = this._trav_.transform(this._state_, toArray(arguments));
478 | return this;
479 | };
480 |
481 | [
482 | 'group'
483 | , 'flatten'
484 | , 'filter'
485 | , 'sort'
486 | , 'pick'
487 | , 'omit'
488 | , 'uniq'
489 | , 'take'
490 | , 'skip'
491 | , 'first'
492 | , 'last'
493 | , 'copy'
494 | , 'pluck'
495 | , 'object'
496 | , 'array'
497 | , 'default' ].forEach(function (name) {
498 | Trans.prototype[name + 'f'] = function () {
499 | var args = toArray(arguments)
500 | , self = this
501 | , field = args[0]
502 | , rest = args.slice(1);
503 |
504 | return this.mapf(field, function (target) {
505 | var t = self._create_(target);
506 | return t[name].apply(t, rest).value();
507 | });
508 | };
509 |
510 | Trans.prototype[name + 'ff'] = function () {
511 | var args = toArray(arguments)
512 | , self = this
513 | , src = args[0]
514 | , dst = args[1]
515 | , rest = args.slice(2);
516 |
517 | return this.mapff(src, dst, function (target) {
518 | var t = self._create_(target);
519 | return t[name].apply(t, rest).value();
520 | });
521 | };
522 | });
523 |
--------------------------------------------------------------------------------
/test/trans/group-test.js:
--------------------------------------------------------------------------------
1 | module.exports = function (util) {
2 | var trans = util.trans
3 | , assert = util.assert
4 | , square = util.square
5 | , mod = util.mod
6 | , sum = util.sum
7 | , add = util.add;
8 |
9 | describe('group', function () {
10 | it('should group an array by the given key - object', function () {
11 | var o = [
12 | { a: { b: 1 } }
13 | , { a: { b: 2 } }
14 | , { a: { b: 2 } }
15 | , { a: { b: 3 } }
16 | , { a: { b: 2 } }
17 | , { a: {} } ]
18 | , t = trans(o).group('a.b').value();
19 | assert.deepEqual(t, [
20 | { key: 1, value: [ { a: { b: 1 } } ] }
21 | , { key: 2, value: [ { a: { b: 2 } }, { a: { b: 2 } }, { a: { b: 2 } } ] }
22 | , { key: 3, value: [ { a: { b: 3 } } ] }
23 | , { key: null, value: [ { a: {} } ] }
24 | ]);
25 | });
26 |
27 | it('should group an array of primitives 1', function () {
28 | var o = [ 1, 1, 3, 3, 2, 1 ]
29 | , t = trans(o).group(null).value();
30 | assert.deepEqual(t, [
31 | { key: 1, value: [ 1, 1, 1 ] }
32 | , { key: 3, value: [ 3, 3 ] }
33 | , { key: 2, value: [ 2 ] }
34 | ]);
35 | });
36 |
37 | it('should group an array of primitives 2', function () {
38 | var o = [ 1, 1, 3, 3, 2, 1 ]
39 | , t = trans(o).group(null, null, square).value();
40 | assert.deepEqual(t, [
41 | { key: 1, value: [ 1, 1, 1 ] }
42 | , { key: 9, value: [ 3, 3 ] }
43 | , { key: 4, value: [ 2 ] }
44 | ]);
45 | });
46 |
47 | it('should group an array of strings', function () {
48 | var o = [ 'abc', 'abc', 'def', 'foo', 'bar', 'foo', 'abc' ]
49 | , t = trans(o).group().value();
50 | assert.deepEqual(t, [
51 | { key: 'abc', value: [ 'abc', 'abc', 'abc' ] }
52 | , { key: 'def', value: [ 'def' ] }
53 | , { key: 'foo', value: [ 'foo', 'foo' ] }
54 | , { key: 'bar', value: [ 'bar' ] }
55 | ]);
56 | });
57 |
58 | it('should make available the array index to the key transformer', function () {
59 | var o = [ 'abc', 'abc', 'def', 'foo', 'bar', 'foo', 'abc' ]
60 | , t = trans(o).group(null, null, function (x) { return x + this.getIndex(); }).value();
61 | assert.deepEqual(t, [
62 | { key: 'abc0', value: [ 'abc' ] }
63 | , { key: 'abc1', value: [ 'abc' ] }
64 | , { key: 'def2', value: [ 'def' ] }
65 | , { key: 'foo3', value: [ 'foo' ] }
66 | , { key: 'bar4', value: [ 'bar' ] }
67 | , { key: 'foo5', value: [ 'foo' ] }
68 | , { key: 'abc6', value: [ 'abc' ] }
69 | ]);
70 | });
71 |
72 | it('should be able to specify the group key and value names', function () {
73 | var o = [ 1, 1, 2, 2, 4 ]
74 | , t = trans(o).group(':num:data').value();
75 | assert.deepEqual(t, [
76 | { num: 1, data: [ 1, 1 ] }
77 | , { num: 2, data: [ 2, 2 ] }
78 | , { num: 4, data: [ 4 ] }
79 | ]);
80 | });
81 |
82 | it('should apply key transformations before grouping', function () {
83 | var o = [ { a: 'foo' }, { a: 'bar' }, { a: 'baz' } ]
84 | , t = trans(o).group('a:letter:obj', null, ['charAt', 1], 'toUpperCase').value();
85 | assert.deepEqual(t, [
86 | { letter: 'O', obj: [ { a: 'foo' } ] }
87 | , { letter: 'A', obj: [ { a: 'bar' }, { a: 'baz' } ] }
88 | ]);
89 | });
90 |
91 | it('should work with nested arrays 1', function () {
92 | var o = [
93 | { a: [ { b: 1 }, { b: 2 } ] }
94 | , { a: [ { b: 2 }, { b: 2 } ] }
95 | , { a: [ { b: 1 }, { b: 3 } ] }
96 | , { a: [ { b: 3 }, { b: 4 } ] } ]
97 | , t = trans(o).group('a.b', null, sum).value();
98 | assert.deepEqual(t, [
99 | { key: 3, value: [ { a: [ { b: 1 }, { b: 2 } ] } ] }
100 | , { key: 4, value: [ { a: [ { b: 2 }, { b: 2 } ] }, { a: [ { b: 1 }, { b: 3 } ] }] }
101 | , { key: 7, value: [ { a: [ { b: 3 }, { b: 4 } ] } ] }
102 | ]);
103 | });
104 |
105 | it('should work with nested arrays 2', function () {
106 | var o = [
107 | [ { a: { b: 1 } }, { a: { b: 2 } } ]
108 | , [ { a: { b: 3 } } ]
109 | , [ { a: { b: 4 } }, { a: { b: 5 } } ] ]
110 | , t = trans(o).group('a.b', null, sum).value();
111 | assert.deepEqual(t, [
112 | { key: 3, value: [
113 | [ { a: { b: 1 } }, { a: { b: 2 } } ], [ { a: { b: 3 } } ] ]
114 | }
115 | , { key: 9, value: [
116 | [ { a: { b: 4 } }, { a: { b: 5 } } ] ]
117 | }
118 | ]);
119 | });
120 |
121 | it('should group an array by the given key - array2', function () {
122 | var o = [ { a: [ 1, 2 ] }, { a: [ 1 ] }, { a: [ 3 ] } ]
123 | , t = trans(o).group('a', null, sum).value();
124 | assert.deepEqual(t, [
125 | { key: 3, value: [ { a: [ 1, 2 ] }, { a: [ 3 ] } ] }
126 | , { key: 1, value: [ { a: [ 1 ] } ] }
127 | ]);
128 | });
129 |
130 | it('should group an array by the given key - array3', function () {
131 | var o = [ { a: [ 1, 2 ] }, { a: [ 1 ] }, { a: [ 2 ] } ]
132 | , t = trans(o).group('a', null, 'length').value();
133 | assert.deepEqual(t, [
134 | { key: 2, value: [ { a: [ 1, 2 ] } ] }
135 | , { key: 1, value: [ { a: [ 1 ] }, { a: [ 2 ] } ] }
136 | ]);
137 | });
138 |
139 | it('should pass missing keys as null through transformers', function () {
140 | var o = [ { a: { b: 1 } }, { a: { b: 2 } }, { a: {} }, { a: { b: 1 } } ]
141 | , t = trans(o).group('a.b', null, [add, 1]).value();
142 | assert.deepEqual(t, [
143 | { key: 2, value: [ { a: { b: 1 } }, { a: { b: 1 } } ] }
144 | , { key: 3, value: [ { a: { b: 2 } } ] }
145 | , { key: 1, value: [ { a: {} } ] }
146 | ]);
147 | });
148 |
149 | it('should group by the items in an array', function () {
150 | var o = [
151 | { a: 1, c: { b: [ 'a', 'b' ] } }
152 | , { a: 2, c: { b: [ 'a' ] } }
153 | , { a: 3, c: { b: [ 'b' ] } }
154 | , { a: 4, c: { b: [ 'c' ] } }
155 | , { a: 5, c: { b: [ 'd', 'e' ] } } ]
156 | , t = trans(o).group('c.b.').value();
157 | assert.deepEqual(t, [
158 | { key: 'a', value: [
159 | { a: 1, c: { b: [ 'a', 'b' ] } }
160 | , { a: 2, c: { b: [ 'a' ] } } ]
161 | }
162 | , { key: 'b', value: [
163 | { a: 1, c: { b: [ 'a', 'b' ] } }
164 | , { a: 3, c: { b: [ 'b' ] } } ]
165 | }
166 | , { key: 'c', value: [ { a: 4, c: { b: [ 'c' ] } } ] }
167 | , { key: 'd', value: [ { a: 5, c: { b: [ 'd', 'e' ] } } ] }
168 | , { key: 'e', value: [ { a: 5, c: { b: [ 'd', 'e' ] } } ] }
169 | ]);
170 | });
171 |
172 | it('should make the array index available when grouping by the items in an array', function () {
173 | var o = [
174 | { a: 1, c: { b: [ 'a', 'b' ] } }
175 | , { a: 2, c: { b: [ 'a' ] } }
176 | , { a: 3, c: { b: [ 'b' ] } }
177 | , { a: 4, c: { b: [ 'c' ] } }
178 | , { a: 5, c: { b: [ 'd', 'e' ] } } ]
179 | , t = trans(o)
180 | .group('c.b.', 'a', function (x) { return x + ':' + this.getIndexes(); })
181 | .value();
182 | assert.deepEqual(t, [
183 | { key: 'a:0,0', value: [ 1 ] }
184 | , { key: 'b:1,0', value: [ 1 ] }
185 | , { key: 'a:0,1', value: [ 2 ] }
186 | , { key: 'b:0,2', value: [ 3 ] }
187 | , { key: 'c:0,3', value: [ 4 ] }
188 | , { key: 'd:0,4', value: [ 5 ] }
189 | , { key: 'e:1,4', value: [ 5 ] }
190 | ]);
191 | });
192 |
193 | it('should group by the items in an array and apply key transformations', function () {
194 | var o = [
195 | { a: [ { b: 1 }, { b: 2 } ] }
196 | , { a: [ { b: 2 }, { b: 2 } ] }
197 | , { a: [ { b: 3 }, { b: 2 } ] } ]
198 | ,t = trans(o).group('a.', null, 'b').value();
199 | assert.deepEqual(t, [
200 | { key: 1, value: [ { a: [ { b: 1 }, { b: 2 } ] } ] }
201 | , { key: 2, value: [
202 | { a: [ { b: 1 }, { b: 2 } ] }
203 | , { a: [ { b: 2 }, { b: 2 } ] }
204 | , { a: [ { b: 3 }, { b: 2 } ] } ]
205 | }
206 | , { key: 3, value: [ { a: [ { b: 3 }, { b: 2 } ] } ] }
207 | ]);
208 | });
209 |
210 |
211 | it('should group by the items in an array with custom names', function () {
212 | var o = [ { a: 1, b: [ 1, 2 ] }, { a: 2, b: [ 1 ] }, { a: 3, b: [ 3 ] } ]
213 | , t = trans(o).group('b.:k:v').value();
214 | assert.deepEqual(t, [
215 | { k: 1, v: [ { a: 1, b: [ 1, 2 ] }, { a: 2, b: [ 1 ] } ] }
216 | , { k: 2, v: [ { a: 1, b: [ 1, 2 ] } ] }
217 | , { k: 3, v: [ { a: 3, b: [ 3 ] } ] }
218 | ]);
219 | });
220 |
221 | it('should be able to specify the group value 1', function () {
222 | var o = [ { a: 1, b: [ 1, 2 ] }, { a: 2, b: [ 1 ] }, { a: 3, b: [ 3 ] } ]
223 | , t = trans(o).group('b.:k:v', 'b').value();
224 | assert.deepEqual(t, [
225 | { k: 1, v: [ [ 1, 2 ], [ 1 ] ] }
226 | , { k: 2, v: [ [ 1, 2 ] ] }
227 | , { k: 3, v: [ [ 3 ] ] }
228 | ]);
229 | });
230 |
231 | it('should be able to specify the group value 2', function () {
232 | var o = [ { a: 1, b: 'A' }, { a: 1, b: 'B' }, { a: 3, b: 'C' } ]
233 | , t = trans(o).group('a:num:letters', 'b').value();
234 | assert.deepEqual(t, [
235 | { num: 1, letters: [ 'A', 'B' ] }
236 | , { num: 3, letters: [ 'C' ] }
237 | ]);
238 | });
239 |
240 | it('should be able to specify the group value 3', function () {
241 | var o = [
242 | { a: 1, b: { c: 'one' } }
243 | , { a: 2, b: { c: 'two' } },
244 | , { a: 3, b: { c: 'three' } },
245 | , { a: 10, b: { c: 'ten' } },
246 | , { a: 11, b: { c: 'eleven' } },
247 | , { a: 20, b: { c: 'twenty' } } ]
248 | , t = trans(o).group('a', 'b.c', [mod, 10]).value();
249 | assert.deepEqual(t, [
250 | { key: 1, value: [ 'one', 'eleven' ] }
251 | , { key: 2, value: [ 'two' ] }
252 | , { key: 3, value: [ 'three' ] }
253 | , { key: 0, value: [ 'ten', 'twenty' ] }
254 | ]);
255 | });
256 |
257 | it('should objectify an array of objects', function () {
258 | var o = [ 1, 2, 1, 1, 3, 0, 0 ]
259 | , t = trans(o)
260 | .map('.', function(x) { return { k: x, v: Boolean(x) }; })
261 | .group('k', 'v')
262 | .value();
263 | assert.deepEqual(t, [
264 | { key: 1, value: [ true, true, true ] }
265 | , { key: 2, value: [ true ] }
266 | , { key: 3, value: [ true ] }
267 | , { key: 0, value: [ false, false ] }
268 | ]);
269 | });
270 |
271 | it('should throw if the group target is not an array', function () {
272 | assert.throws(function() {
273 | trans({ a: 1 }).group('a');
274 | }, /the group target is not an array/i);
275 | });
276 | });
277 |
278 | describe('groupf', function () {
279 | it('should group the object at the given field - object', function () {
280 | var o = { a: { b: [ 1, 1, 3, 3, 1, 2 ] } }
281 | , e = { a: { b: [
282 | { key: 1, value: [ 1, 1, 1 ] }
283 | , { key: 9, value: [ 3, 3 ] }
284 | , { key: 4, value: [ 2 ] }
285 | ] } }
286 | , t = trans(o).groupf('a.b', null, null, square).value();
287 | assert.deepEqual(t, e);
288 | });
289 |
290 | it('should group the object at the given field - array1', function () {
291 | var o = { a: [ { b: [ 1, 1, 2 ] }, { b: [ 3, 3 ] }, { b: [ 1, 2, 3 ] } ] }
292 | , e = { a: [
293 | { b: [ { key: 1, value: [ 1, 1 ] }, { key: 2, value: [ 2 ] } ] }
294 | , { b: [ { key: 3, value: [ 3, 3 ] } ] }
295 | , { b: [ { key: 1, value: [ 1 ] }, { key: 2, value: [ 2 ] }, { key: 3, value: [ 3 ] } ] }
296 | ] }
297 | , t = trans(o).groupf('a.b').value();
298 | assert.deepEqual(t, e);
299 | });
300 |
301 | it('should group the object at the given field - array2', function () {
302 | var o = { a: [ { b: [ 1, 1, 2 ] }, { b: [ 3, 3 ] }, { b: [ 1, 2, 3 ] } ] }
303 | , e = { a: [
304 | { key: 3, value: [ { b: [ 1, 1, 2 ] }, { b: [ 1, 2, 3 ] } ] }
305 | , { key: 2, value: [ { b: [ 3, 3 ] } ] }
306 | ]}
307 | , t = trans(o).groupf('a', 'b', null, 'length').value();
308 | assert.deepEqual(t, e);
309 | });
310 |
311 | it('should handle nested arrays', function () {
312 | var o = [
313 | { a: [ { b: 1 }, { b: 2 }, { b: 3 } ] }
314 | , { a: [ { b: 3 }, { b: 3 }, { b: 3 } ] }
315 | , { a: [ { b: 4 } ] }
316 | ]
317 | , t = trans(o).groupf('a', 'b:k:v', null, [mod, 2]).value();
318 | assert.deepEqual(t, [
319 | { a: [ { k: 1, v: [ { b: 1 }, { b: 3 } ] }, { k: 0, v: [ { b: 2 } ] } ] }
320 | , { a: [ { k: 1, v: [ { b: 3 }, { b: 3 }, { b: 3 } ] } ] }
321 | , { a: [ { k: 0, v: [ { b: 4 } ] } ] }
322 | ]);
323 | });
324 | });
325 |
326 | describe('groupff', function () {
327 | it('should group the target value and set it on the destination field', function () {
328 | var o = { a: [ 1, 2, 1, 1 ], b: { c: 1 } }
329 | , t = trans(o).groupff('a', 'b.c', ':k:v', null, [add, 1]).value();
330 | assert.deepEqual(t, {
331 | a: [ 1, 2, 1, 1 ]
332 | , b: { c: [ { k: 2, v: [ 1, 1, 1 ] }, { k: 3, v: [ 2 ] } ] }
333 | });
334 | });
335 |
336 | it('should work with nested arrays', function () {
337 | var o = [
338 | { a: [ 1, 2, 3 ] }
339 | , { a: [ 11, 12, 13, 14 ] }
340 | , { a: [ 99, 98, 77 ] } ]
341 | , t = trans(o).groupff('a', 'c', null, null, [mod, 2]).value();
342 | assert.deepEqual(t, [
343 | {
344 | a: [ 1, 2, 3 ]
345 | , c: [ { key: 1, value: [ 1, 3 ] }, { key: 0, value: [ 2 ] } ]
346 | }
347 | , {
348 | a: [ 11, 12, 13, 14 ]
349 | , c: [ { key: 1, value: [ 11, 13 ] }, { key: 0, value: [ 12, 14 ] } ]
350 | }
351 | , {
352 | a: [ 99, 98, 77 ]
353 | , c: [ { key: 1, value: [ 99, 77 ] }, { key: 0, value: [ 98 ] } ]
354 | }
355 | ]);
356 | });
357 | });
358 | };
359 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ```
2 | __
3 | _/ |_____________ ____ ______
4 | \ __\_ __ \__ \ / \ / ___/
5 | | | | | \// __ \| | \\___ \
6 | |__| |__| (____ /___| /____ >
7 | \/ \/ \/
8 | ```
9 |
10 | [](https://www.npmjs.com/package/trans)
11 | [](https://www.npmjs.com/package/trans)
12 | [](https://travis-ci.org/gabesoft/trans)
13 |
14 |
15 | *The ultimate object transformer*
16 |
17 | ## Install
18 |
19 | ```
20 | $ npm install trans
21 | ```
22 |
23 | ## Purpose
24 |
25 | The purpose of trans is to make it super easy to transform complex json objects
26 |
27 | ## Overview
28 |
29 | Trans allows specifying composite field names such as ``a.b.c`` and it does the
30 | right thing even across multiple arrays.
31 | For example, the field above could be used to modify or extract a value from an object
32 | that looks like this
33 |
34 | ``` javascript
35 | { a: { b: { c: 1 } } }
36 | ```
37 | but also if the object looks like this
38 | ``` javascript
39 | { a: [ { b: { c: 1 } }, { b: { c: 2 } } ] }
40 | ```
41 | or like this
42 | ``` javascript
43 | [ { a: { b: [ { c: 1 }, { c: 2 } ] } } ]
44 | ```
45 |
46 | There are three types of transformation methods:
47 | - ``map(*transformers)`` transforms the entire object
48 | - ``mapf(field, *transformers)`` transforms the value of a field
49 | - ``mapff(source, destination, *transformers)`` transforms the value of a field and
50 | sets it onto another field
51 |
52 | The transformers specified as parameters to the transformation methods can be functions,
53 | field names, or even objects (which will be used as hash maps). The functions that are not properties
54 | on the object being transformed are assumed to take that object as the first parameter. But, they can
55 | take additional parameters as well. In those case the function should be specified as an array.
56 | When multiple transformers are specified the result of each one is piped over to the next one.
57 |
58 | Here are a couple of examples which result in an identical outcome:
59 | ``` javascript
60 | trans({ a: [ 1, 2 ] }).mapf('a', 'length', [add, 5], [mul, 10], square);
61 | ```
62 | ``` javascript
63 | trans({ a: [ 1, 2 ] }).mapf('a', function(obj) {
64 | return square(mul(add(obj.length, 5), 10));
65 | });
66 | ```
67 | The result in both cases is:
68 | ``` javascript
69 | { a: 4900 }
70 | ```
71 |
72 | ## Quickstart
73 |
74 | Using trans is easy, first wrap the data to be transformed by calling ``trans(data)``,
75 | as below, and then call transformation methods on the wrapper. Multiple transformation
76 | methods can be chained. When done call ``value()`` to get back the raw data that has been transformed.
77 |
78 | Here's a quick taste. Assuming we have an object that looks like this
79 |
80 | ``` javascript
81 | var data = [
82 | { a: { b: 'fbc' }, c: 1 }
83 | , { a: { b: 'foo' }, c: 3 }
84 | , { a: { b: 'fde' }, c: 2 }
85 | , { a: { b: 'def' }, c: 3 }
86 | , { a: { b: 'ghk' }, c: 4 } ];
87 | ```
88 |
89 | We can use trans to group the data array by the first letter capitalized of the ``a.b`` field
90 | and set the group value to ``c``, then sort the value array, and finally sort the
91 | entire result array by the group key as follows
92 |
93 | ``` javascript
94 | var trans = require('trans');
95 | var result = trans(data)
96 | .group('a.b', 'c', ['charAt', 0], 'toUpperCase')
97 | .sortf('value')
98 | .sort('key')
99 | .value();
100 |
101 | ```
102 |
103 | After running the above code ``result`` will have the following value
104 |
105 | ``` javascript
106 | [ { key: 'D', value: [ 3 ] }, { key: 'F', value: [ 1, 2, 3 ] }, { key: 'G', value: [ 4 ] } ]
107 | ```
108 |
109 |
110 | ## Methods (index)
111 |
112 | * [map(*transformers)](#mapfn)
113 | * [mapf(field, *transformers)](#mapffn)
114 | * [mapff(source, destination, *transformers)](#mapfffn)
115 | * [group(groupField, valueField, *key-transformers)](#groupfn)
116 | * [sort(sortField, *transformers, \[comparer\])](#sortfn)
117 | * [object(keyField, valueField, *key-transformers)](#objectfn)
118 | * [array(keyName, valueName)](#arrayfn)
119 | * [filter(filterField, *transformers)](#filterfn)
120 | * [flatten(deep)](#flattenfn)
121 | * [default(key1, value1, key2, value2, ...)](#defaultfn)
122 | * [pick(*fields)](#pickfn)
123 | * [omit(*fields)](#omitfn)
124 | * [remove(*fields)](#removefn)
125 | * [pluck(field, *transformers)](#pluckfn)
126 | * [skip(count)](#skipfn)
127 | * [take(count)](#takefn)
128 | * [first()](#firstfn)
129 | * [last()](#lastfn)
130 | * [uniq(uniqField, *transformers)](#uniqfn)
131 |
132 |
133 | ## Methods (detail)
134 |
135 | ``` javascript
136 | var trans = require('trans');
137 | ```
138 |
139 | ### trans(data)
140 | This function creates a transformation wrapper around the specified data.
141 | Further transformation methods can be chained on the trans wrapper.
142 |
143 | ### value()
144 | Unwraps the raw data object.
145 |
146 | ### get(callback)
147 | Makes the current raw data available for inspection. It can be used
148 | to insert console log statements in the transformation chain for debugging purposes.
149 |
150 | ``` javascript
151 | var value = trans(data)
152 | .group('a.b')
153 | .get(console.log)
154 | .sort('key')
155 | .value();
156 | ```
157 |
158 | ### map(*transformers)
159 |
160 | This is the main transformation method. It passes the entire raw object to the transformers
161 | and it replaces it with the result returned by the last transformer function.
162 |
163 | Here is a simple example:
164 |
165 | ``` javascript
166 | trans('2.32').map(parseFloat, square, [ add, 10 ]).value();
167 | ```
168 | => ``15.3824``
169 |
170 | Field names, and functions that exist on the object being transformed
171 | can be specified as transformers
172 |
173 | ``` javascript
174 | trans('transform me').map('toUpperCase', [ 'substring', 0, 5 ]).value();
175 | ```
176 | => ``'TRANS'``
177 |
178 | ``` javascript
179 | trans({ a: 1 }).map('a').value();
180 | ```
181 | => ``1``
182 |
183 | ``` javascript
184 | trans({ a: 'foo' }).map('a', 'toUpperCase', [ 'charAt', 1 ]).value();
185 | ```
186 | => ``'O'``
187 |
188 | If the current object is an array the whole array is passed to the transformer functions.
189 | To transform its elements instead precede the transformers with a dot ``'.'`` which will
190 | indicate that array iteration is desired.
191 |
192 | Here are a few array examples:
193 |
194 | ``` javascript
195 | trans([ 1, 2, 3 ]).map('length').value();
196 | ```
197 | => ``3``
198 |
199 | ``` javascript
200 | trans([ 1, 2, 3 ]).map('.', square).value();
201 | ```
202 | => ``[ 1, 4, 9 ]``
203 |
204 | ``` javascript
205 | trans([ [ 1, 2 ], [ 3 ], [ 4, 5, 6 ] ]).map('.', sum).value();
206 | ```
207 | => ``[ 3, 3, 15 ]``
208 |
209 | ``` javascript
210 | trans([ [ 1, 2 ], [ 3 ], [ 4, 5, 6 ] ]).map('.', '.', [ add, 5 ]).value();
211 | ```
212 | => ``[ [ 6, 7 ], [ 8 ], [ 9, 10, 11 ] ]``
213 |
214 | ``` javascript
215 | trans([ { a: [ 1, 2 ] }, { a: [ 3 ] }, { a: [ 4, 5, 6 ] } ])
216 | .map('.', 'a', 'length')
217 | .value();
218 | ```
219 | => ``[ 2, 1, 3 ]``
220 |
221 | ``` javascript
222 | trans([ { a: [ 1, 2 ] }, { a: [ 3 ] }, { a: [ 4, 5, 6 ] } ])
223 | .map('.', 'a', '.', square)
224 | .value();
225 | ```
226 | => ``[ [ 1, 4 ], [ 9 ], [ 16, 25, 36 ] ]``
227 |
228 | Objects can be specified as transformers as well. When that is the case the result of
229 | the previous transformation will be used as an index into the transformer object.
230 |
231 | ``` javascript
232 | var intToName = { 1: 'one', 2: 'two', 3: 'three' };
233 |
234 | trans([ 1, 2 ]).map('length', intToName).value();
235 | ```
236 | => ``'two'``
237 |
238 | [Back to Index](#methodsindex)
239 |
240 |
241 | ### mapf(field, *transformers)
242 |
243 | This is exactly like ``map`` but it is applied at a specified field. In fact if a null
244 | field is specified the result is identical to calling ``map``. Otherwise, the input
245 | to the first transformer function will be the value at the specified field and the result
246 | of the last transformer will replace the value at that field.
247 |
248 | ``` javascript
249 | trans(1).mapf(null, [ add, 1 ]).value();
250 | ```
251 | => ``2``
252 |
253 | ``` javascript
254 | trans({ a: 1 }).mapf('a', [ add, 1 ]).value();
255 | ```
256 | => ``{ a: 2 }``
257 |
258 | Field names can contain dots to reach within nested objects.
259 |
260 | ``` javascript
261 | trans({ a: { b: 1 } }).mapf('a.b', [ add, 1 ]).value();
262 | ```
263 | => ``{ a: { b: 2 } }``
264 |
265 | Such field names work across arrays as well.
266 |
267 | ``` javascript
268 | trans({ a: [ { b: 1 }, { b: 2 } ] }).mapf('a.b', [ add, 1 ]).value();
269 | ```
270 | => ``{ a: [ { b: 2 }, { b: 3 } ] }``
271 |
272 | If the value at the field is an array the entire array is passed to the transformer functions.
273 |
274 | ``` javascript
275 | trans({ a: { b: [ 1, 2 ] } }).mapf('a.b', 'length').value();
276 | ```
277 | => ``{ a: { b: 2 } }``
278 |
279 | Append one last dot to the end of the field name to indicate that array iteration
280 | is desired. In such a case each array item is passed to the transformer functions and a
281 | new array is created with the results of the transformations.
282 |
283 | ``` javascript
284 | trans({ a: { b: [ 1, 2 ] } }).mapf('a.b.', [ add, 1 ]).value();
285 | ```
286 | => ``{ a: { b: [ 2, 3 ] } }``
287 |
288 | Specifying a dot ``'.'`` as the first transformer accomplishes the same thing.
289 |
290 | ``` javascript
291 | trans({ a: { b: [ 1, 2 ] } }).mapf('a.b', '.', [ add, 1 ]).value();
292 | ```
293 | => ``{ a: { b: [ 2, 3 ] } }``
294 |
295 | [Back to Index](#methodsindex)
296 |
297 |
298 | ### mapff(source, destination, *transformers)
299 |
300 | This transformation maps the value of a field and sets the result onto another field.
301 | If the destination is null, the entire object is replaced. If the source and destination are both null it has the exact
302 | same outcome as ``map``. If the destination field does not exist it is created, otherwise its
303 | value is replaced by the result of the transformation. The source field is left unchanged.
304 |
305 | ``` javascript
306 | trans({ a: 1 }).mapff('a', 'b').value();
307 | ```
308 | => ``{ a: 1, b: 1 }``
309 |
310 | ``` javascript
311 | trans({ a: 1 }).mapff('a', 'b', [ add, 1 ], square).value();
312 | ```
313 | => ``{ a: 1, b: 4 }``
314 |
315 | Composite fields are allowed but the value passed to transformers is scoped based on where
316 | the source and destination fields point to. This becomes relevant when we are transforming across
317 | arrays.
318 |
319 | Below the function ``sum`` gets an array containing the values of ``a.b``, in this case ``[ 1, 2 ]``
320 | and it computes their sum.
321 |
322 | ``` javascript
323 | trans({ a: [ { b: 1 }, { b: 2 } ] }).mapff('a.b', 'c', sum).value();
324 | ```
325 | => ``{ a: [ { b: 1 }, { b: 2 } ], c: 3 }``
326 |
327 | In the next example the scope is reduced to each object inside the array, so the transformers
328 | only get the value of the ``b`` field.
329 |
330 | ``` javascript
331 | trans({ a: [ { b: 1 }, { b: 2 } ] }).mapff('a.b', 'a.c', [ add, 1 ]).value();
332 | ```
333 | => ``{ a: [ { b: 1, c: 2 }, { b: 2, c: 3 } ] }``
334 |
335 | Same thing below, the scope is each item in the array due to the destination field
336 | pointing to items in the array.
337 |
338 | ``` javascript
339 | trans({ a: [ { b: 1, c: 3 }, { b: 2, c: 3 } ] })
340 | .mapff('a', 'a.d', function(a) { return a.b + a.c; })
341 | .value();
342 | ```
343 | => ``{ a: [ { b: 1, c: 3, d: 4 }, { b: 2, c: 3, d: 5 } ] }``
344 |
345 | Constrast the above with the next example where the destination is a field
346 | on the outer object. The scope now is the entire array that ``a`` points to.
347 |
348 | ``` javascript
349 | trans({ a: [ { b: 1, c: 3 }, { b: 2, c: 3 } ] })
350 | .mapff('a', 'd', '.', 'b')
351 | .value();
352 | ```
353 | => ``{ a: [ { b: 1, c: 3 }, { b: 2, c: 3 } ], d: [ 1, 2 ] }``
354 |
355 | If the source field points to an array we can indicate that we want to transform the elements of
356 | the array by appending one last dot to it. Alternatively, a dot ``'.'`` could be specified as
357 | the first transformer.
358 |
359 | ``` javascript
360 | trans({ a: { b: [ 1, 2, 3 ] } }).mapff('a.b', 'a.c', 'length').value();
361 | ```
362 | => ``{ a: { b: [ 1, 2, 3 ], c: 3 } }``
363 |
364 | ``` javascript
365 | trans({ a: { b: [ 1, 2, 3 ] } }).mapff('a.b.', 'a.c', [ add, 5 ]).value();
366 | ```
367 | => ``{ a: { b: [ 1, 2, 3 ], c: [ 6, 7, 8 ] } }``
368 |
369 | ``` javascript
370 | trans({ a: { b: [ 1, 2, 3 ] } }).mapff('a.b', 'a.c', '.', [ add, 5 ]).value();
371 | ```
372 | => ``{ a: { b: [ 1, 2, 3 ], c: [ 6, 7, 8 ] } }``
373 |
374 | If the destination is null the entire object is replaced. This could be useful for picking up values,
375 | although, there is a ``pluck`` method for this purpose.
376 |
377 | ``` javascript
378 | trans([ { a: [ { b: 1 }, { b: 2 } ] }, { a: [ { b: 3 } ] } ])
379 | .mapff('a.b', null)
380 | .value();
381 | ```
382 | => ``[ [ 1, 2 ], [ 3 ] ]``
383 |
384 | See the [unit tests](https://github.com/gabesoft/trans/blob/master/test/trans/map-test.js)
385 | for additional examples.
386 |
387 | [Back to Index](#methodsindex)
388 |
389 |
390 | ### group(groupField, valueField, *key-transformers)
391 |
392 | Maps an array of objects into an array of key-value pairs where the key is the value
393 | of the specified group field (possibly transformed) and the value is an array of values as
394 | indicated by the value field. If the value field is null the entire array item is used.
395 |
396 | ``` javascript
397 | trans([ 'ray', 'rich', 'charles' ]).group(null, null, [ 'charAt', 0 ]).value();
398 | ```
399 | => ``[ { key: 'r', value: [ 'ray', 'rich' ] }, { key: 'c', value: [ 'charles' ] } ]``
400 |
401 | ``` javascript
402 | trans([ { a: 'ray', b: 1 }, { a: 'rich', b: 2 }, { a: 'charles', b: 3 } ])
403 | .group('a', 'b', [ 'charAt', 0 ], 'toUpperCase')
404 | .value();
405 | ```
406 | => ``[ { key: 'R', value: [ 1, 2 ] }, { key: 'C', value: [ 3 ] } ]``
407 |
408 | The default key and value names in the output array can be overriden with different names
409 | specified as part of the group field. The syntax of the group field is
410 | ``field:keyName:valueName``.
411 |
412 | ``` javascript
413 | trans([ 1, 1, 2, 1 ]).group(':k:v', null).value();
414 | ```
415 | => ``[ { k: 1, v: [ 1, 1, 1 ] }, { k: 2, v: [ 2 ] } ]``
416 |
417 | ``` javascript
418 | trans([ { a: 1, b: 'x' }, { a: 1, b: 'y' }, { a: 2, b: 'z' } ])
419 | .group('a:number:letters', 'b')
420 | .value();
421 | ```
422 | => ``[ { number: 1, letters: [ 'x', 'y' ] }, { number: 2, letters: [ 'z' ] } ]``
423 |
424 | The group field name can contain dots to reach within nested objects or arrays.
425 |
426 | ``` javascript
427 | trans([ { a: [ { b: 1 }, { b: 2 } ], c: 'three' }, { a: [ { b: 10 } ], c: 'ten' } ])
428 | .group('a.b', 'c')
429 | .value();
430 | ```
431 | => ``[ { key: [ 1, 2 ], value: [ 'three' ] }, { key: [ 10 ], value: [ 'ten' ] } ]``
432 |
433 | ``` javascript
434 | trans([ { a: [ { b: 1 }, { b: 2 } ], c: 'three' }, { a: [ { b: 10 } ], c: 'ten' } ])
435 | .group('a.b', 'c', sum)
436 | .value();
437 | ```
438 | => ``[ { key: 3, value: [ 'three' ] }, { key: 10, value: [ 'ten' ] } ]``
439 |
440 | ``` javascript
441 | trans([ { a: { b: 1, c: 'one' } }, { a: { b: 11, c: 'eleven' } }, { a: { b: 2, c: 'two' } } ])
442 | .group('a.b', 'a.c', [ mod, 10 ])
443 | .value();
444 | ```
445 | => ``[ { key: 1, value: [ 'one', 'eleven' ] }, { key: 2, value: [ 'two' ] } ]``
446 |
447 | #### Other versions
448 | - ``groupf(field, groupField, valueField, *key-transformers)``
449 | - ``groupff(source, destination, groupField, valueField, *key-transformers)``
450 |
451 | [Back to Index](#methodsindex)
452 |
453 |
454 | ### sort(sortField, *transformers, [comparer])
455 |
456 | Replaces the target array with a stable sorted copy based on the value at the sort field
457 | (possibly transformed). If the last argument is a function that takes exactly two arguments
458 | it will be used as a comparer, otherwise a default comparer will be used.
459 |
460 | ``` javascript
461 | trans([ 1, 2, 1, 1, 3 ]).sort(null).value();
462 | ```
463 | => ``[ 1, 1, 1, 2, 3 ]``
464 |
465 | ``` javascript
466 | trans([ 'Ash', 'bar', 'Baz', 'baz', 'bak', 'Foo', 'ash' ]).sort(null, 'toUpperCase').value();
467 | ```
468 | => ``[ 'Ash', 'ash', 'bak', 'bar', 'Baz', 'baz', 'Foo' ]``
469 |
470 | ``` javascript
471 | var intToName = { 1: 'one', 2: 'two', 3: 'three', 4: 'four', 5: 'five' };
472 |
473 | trans([ 1, 2, 3, 4, 5 ]).sort(null, intToName).value();
474 | ```
475 | => ``[ 5, 4, 1, 3, 2 ]``
476 |
477 | ``` javascript
478 | trans([ { a: 1 }, { a: 3 }, { a: 2 } ]).sort('a').value();
479 | ```
480 | => ``[ { a: 1 }, { a: 2 }, { a: 3 } ]``
481 |
482 | ``` javascript
483 | trans([
484 | { a: 1, b: { c: 'one' } }
485 | , { a: 3, b: { c: 'three' } }
486 | , { a: 10, b: { c: 'ten' } }
487 | , { a: 2, b: { c: 'two' } } ])
488 | .sort('b.c')
489 | .value();
490 | ```
491 | =>
492 | ``` javascript
493 | [ { a: 1, b: { c: 'one' } },
494 | { a: 10, b: { c: 'ten' } },
495 | { a: 3, b: { c: 'three' } },
496 | { a: 2, b: { c: 'two' } } ]
497 | ```
498 |
499 | The sort direction can be specified on the sort field.
500 | The sort field syntax is ``name:direction``
501 |
502 | ``` javascript
503 | trans([ 1, 1, 3, 2 ]).sort(':descending').value();
504 | ```
505 | => ``[ 3, 2, 1, 1 ]``
506 |
507 | ``` javascript
508 | trans([ { a: { b: 1 } }, { a: { b: 1 } }, { a: { b: 3 } }, { a: { b: 2 } } ])
509 | .sort('a.b:descending')
510 | .value();
511 | ```
512 | => ``[ { a: { b: 3 } }, { a: { b: 2 } }, { a: { b: 1 } }, { a: { b: 1 } } ]``
513 |
514 | See the [unit tests](https://github.com/gabesoft/trans/blob/master/test/trans/sort-test.js)
515 | for additional examples.
516 |
517 | #### Other versions
518 | - ``sortf(field, sortField, *transformers, [comparer])``
519 | - ``sortff(source, destination, sortField, *transformers, [comparer])``
520 |
521 | [Back to Index](#methodsindex)
522 |
523 |
524 | ### object(keyField, valueField, *key-transformers)
525 |
526 | Transforms an array into an object where the key is the value at the specified key field
527 | (possibly transformed) and the value is as indicated by the value field. If the value
528 | field is null, the entire array item is used as the value. If multiple values map to the
529 | same key, the last one wins.
530 |
531 | ``` javascript
532 | trans(['abc', 'def', 'ghk']).object(null, null, [ 'charAt', 0 ]).value();
533 | ```
534 | => ``{ a: 'abc', d: 'def', g: 'ghk' }``
535 |
536 | ``` javascript
537 | trans([ { a: 'abc', b: 1 }, { a: 'def', b: 2 }, { a: 'ghk', b: 3 } ])
538 | .object('a', 'b', [ 'charAt', 1 ], 'toUpperCase')
539 | .value();
540 | ```
541 | => ``{ B: 1, E: 2, H: 3 }``
542 |
543 | ``` javascript
544 | trans([ { a: 'abc', b: 1 }, { a: 'def', b: 2 }, { a: 'ghk', b: 3 } ])
545 | .object('a', null, [ 'charAt', 1 ], 'toUpperCase')
546 | .value();
547 | ```
548 | => ``{ B: { a: 'abc', b: 1 }, E: { a: 'def', b: 2 }, H: { a: 'ghk', b: 3 } }``
549 |
550 | See the [unit tests](https://github.com/gabesoft/trans/blob/master/test/trans/object-test.js)
551 | for additional examples.
552 |
553 | #### Other versions
554 | - ``objectf(field, keyField, valueField, *key-transformers)``
555 | - ``objectff(source, destination, keyField, valueField, *key-transformers)``
556 |
557 | [Back to Index](#methodsindex)
558 |
559 |
560 | ### array(keyName, valueName)
561 |
562 | Transforms an object into an array where each item is a key-value pair containing each key
563 | and its value. The key and value names can be specified, otherwise they will default
564 | to ``key`` and ``value``.
565 |
566 | ``` javascript
567 | trans({ a: 1, b: 2, c: 3 }).array().value();
568 | ```
569 | => ``[ { key: 'a', value: 1 }, { key: 'b', value: 2 }, { key: 'c', value: 3 } ]``
570 |
571 | ``` javascript
572 | trans({ a: 1, b: 2, c: 3 }).array('letter', 'number').value();
573 | ```
574 | => ``[ { letter: 'a', number: 1 }, { letter: 'b', number: 2 }, { letter: 'c', number: 3 } ]``
575 |
576 | If the target object is an array, ``array`` will be applied to every item in the array.
577 |
578 | ``` javascript
579 | trans([ { a: 1, b: 2 }, { a: 3, b: 4, c: 5 }, { d: 6 } ]).array('k', 'v').value();
580 | ```
581 | =>
582 | ``` javascript
583 | [ [ { k: 'a', v: 1 }, { k: 'b', v: 2 } ],
584 | [ { k: 'a', v: 3 }, { k: 'b', v: 4 }, { k: 'c', v: 5 } ],
585 | [ { k: 'd', v: 6 } ] ]
586 | ```
587 |
588 | #### Other versions
589 | - ``arrayf(field, keyName, valueName)``
590 | - ``arrayff(source, destination, keyName, valueName)``
591 |
592 | [Back to Index](#methodsindex)
593 |
594 |
595 | ### filter(filterField, *transformers)
596 |
597 | Filters an array by the value at the specified field (possibly transformed). If the result
598 | of the last transformer is not a boolean value, it will be converted to one before filtering.
599 |
600 | ``` javascript
601 | trans([1, 2, 1, 4, 5]).filter(null, [ mod, 2 ]).value();
602 | ```
603 | => ``[ 1, 1, 5 ]``
604 |
605 | ``` javascript
606 | trans([ { a: { b: 1 } }, { a: { b: 0 } }, { a: { b: 3 } } ]).filter('a.b').value();
607 | ```
608 | => ``[ { a: { b: 1 } }, { a: { b: 3 } } ]``
609 |
610 | ``` javascript
611 | trans([ { a: 'real' }, { a: 'rock' }, { a: 'star' } ])
612 | .filter('a', function (s) { return s.charAt(0) === 'r'; })
613 | .value();
614 | ```
615 | => ``[ { a: 'real' }, { a: 'rock' } ]``
616 |
617 | The filtering can be inverted by indicating invert on the filter field.
618 |
619 | ``` javascript
620 | trans([1, 2, 1, 4, 5]).filter(':invert', [ mod, 2 ]).value();
621 | ```
622 | => ``[ 2, 4 ]``
623 |
624 | ``` javascript
625 | trans([ { a: 'real' }, { a: 'rock' }, { a: 'star' } ])
626 | .filter('a:invert', [ 'charAt', 0 ], [ 'localeCompare', 'r' ])
627 | .value();
628 | ```
629 | => ``[ { a: 'real' }, { a: 'rock' } ]``
630 |
631 | #### Other versions
632 | - ``filterf(field, filterField, *transformers)``
633 | - ``filterff(source, destination, filterField, *transformers)``
634 |
635 | [Back to Index](#methodsindex)
636 |
637 |
638 | ### flatten(deep)
639 |
640 | Flattens nested arrays. If deep is set to false or not specified only the first level
641 | is flattened.
642 |
643 | ``` javascript
644 | trans([ [ 1 ], [ 2 ], [ [ 3, 4 ], 5 ], [ [ 6 ] ] ]).flatten().value();
645 | ```
646 | => ``[ 1, 2, [ 3, 4 ], 5, [ 6 ] ]``
647 |
648 | ``` javascript
649 | trans([ [ 1 ], [ 2 ], [ [ 3, 4 ], 5 ], [ [ 6 ] ] ]).flatten(true).value();
650 | ```
651 | => ``[ 1, 2, 3, 4, 5, 6 ]``
652 |
653 | #### Other versions
654 | - ``flatenf(field, deep)``
655 | - ``flatenff(source, destination, deep)``
656 |
657 | [Back to Index](#methodsindex)
658 |
659 |
660 | ### default(key1, value1, key2, value2, ...)
661 |
662 | Fills in ``undefined`` and ``null`` values with the specified defaults. The number of
663 | arguments is expected to be an even number.
664 |
665 | ``` javascript
666 | trans({ a: { b: null, c: { }, d: { e: { g: 4 } } } })
667 | .default('a.b', 1, 'a.c.f', 2, 'a.d.e.f', 5)
668 | .value();
669 | ```
670 | => ``{ a: { b: 1, c: { f: 2 }, d: { e: { f: 5, g: 4 } } } }``
671 |
672 |
673 | If the target object is an array, ``default`` is applied to every item in the array.
674 |
675 | ``` javascript
676 | trans([{}, { a: 1 }, { a: null }, { a: 4 }, {} ]).default('a', 100).value();
677 | ```
678 | => ``[ { a: 100 }, { a: 1 }, { a: 100 }, { a: 4 }, { a: 100 } ]``
679 |
680 | ``` javascript
681 | trans([ { a: [ { b: 1 }, {} ] }, { a: [ {} ] } ]).default('a.b', 10).value();
682 | ```
683 | => ``[ { a: [ { b: 1 }, { b: 10 } ] }, { a: [ { b: 10 } ] } ]``
684 |
685 | [Back to Index](#methodsindex)
686 |
687 |
688 | ### pick(*fields)
689 |
690 | Creates new objects that contain only the specified fields.
691 |
692 | ``` javascript
693 | trans({ a: { b: 1, c: 2 }, d: 5, e: 6 }).pick('a.b', 'e').value();
694 | ```
695 | => ``{ a: { b: 1 }, e: 6 }``
696 |
697 | If the target object is an array, ``pick`` is applied to every item in the array.
698 |
699 | ``` javascript
700 | trans({ a: [ { b: 1, c: 2 }, { b: 3, c: 4 } ], d: 5 }).pick('a.b').value();
701 | ```
702 | => ``{ a: [ { b: 1 }, { b: 3 } ] }``
703 |
704 | #### Other versions
705 | - ``pickf(field, *fields)``
706 | - ``pickff(source, destination, *fields)``
707 |
708 | [Back to Index](#methodsindex)
709 |
710 |
711 | ### omit(*fields)
712 |
713 | Creates new objects that do not contain the specified fields. Calling omit with no
714 | parameters will create a deep copy of the current object.
715 |
716 | ``` javascript
717 | trans({ a: { b: 1, c: 2 }, d: 5, e: 6 }).omit('a.c', 'd').value();
718 | ```
719 | => ``{ a: { b: 1 }, e: 6 }``
720 |
721 | If the target object is an array, ``omit`` is applied to every item in the array.
722 |
723 | ``` javascript
724 | trans({ a: [ { b: 1, c: 2 }, { b: 3, c: 4 } ], d: 5 }).omit('a.c', 'd').value();
725 | ```
726 | => ``{ a: [ { b: 1 }, { b: 3 } ] }``
727 |
728 | #### Other versions
729 | - ``omitf(field, *fields)``
730 | - ``omitff(source, destination, *fields)``
731 |
732 | [Back to Index](#methodsindex)
733 |
734 |
735 | ### remove(*fields)
736 |
737 | Traverses the object tree and deletes the specified fields in place.
738 |
739 | ``` javascript
740 | trans({ a: { b: 1, c: 2 }, d: 5, e: 6 }).remove('a.c', 'd').value();
741 | ```
742 | => ``{ a: { b: 1 }, e: 6 }``
743 |
744 | If the target object is an array, ``remove`` is applied to every item in the array.
745 |
746 | ``` javascript
747 | trans({ a: [ { b: 1, c: 2 }, { b: 3, c: 4 } ], d: 5 }).remove('a.c', 'd').value();
748 | ```
749 | => ``{ a: [ { b: 1 }, { b: 3 } ] }``
750 |
751 | [Back to Index](#methodsindex)
752 |
753 |
754 | ### pluck(pluckField, *transformers)
755 |
756 | Extracts the value(s) at the indicated field.
757 |
758 | ``` javascript
759 | trans({ a: { b: 100 } }).pluck('a.b').value();
760 | ```
761 | => ``100``
762 |
763 | ``` javascript
764 | trans({ a: [ { b: 1 }, { b: 2 }, { b: 3 } ] }).pluck('a.b').value();
765 | ```
766 | => ``[ 1, 2, 3 ]``
767 |
768 | ``` javascript
769 | trans({ a: [ { b: 1 }, { b: 2 }, { b: 3 } ] }).pluck('a.b', sum).value();
770 | ```
771 | => ``6``
772 |
773 | ``` javascript
774 | trans({ a: [ { b: 1 }, { b: 2 }, { b: 3 } ] }).pluck('a.b', '.', [ add, 1 ]).value();
775 | ```
776 | => ``[ 2, 3, 4 ]``
777 |
778 | ``` javascript
779 | trans([ { a: { b: [ { c: 1 } ] } }, { a: { b: [ { c: 3 }, { c: 4 } ] } } ])
780 | .pluck('a.b.c')
781 | .value();
782 | ```
783 | => ``[ [ 1 ], [ 3, 4 ] ]``
784 |
785 | #### Other versions
786 | - ``pluckf(field, pluckField, *transformers)``
787 | - ``pluckff(source, destination, pluckField, *transformers)``
788 |
789 | [Back to Index](#methodsindex)
790 |
791 |
792 | ### skip(count)
793 |
794 | Creates a new array that skips the specified number of items.
795 |
796 | ``` javascript
797 | trans([ 1, 2, 3, 4, 5, 6 ]).skip(2).value();
798 | ```
799 | => ``[ 3, 4, 5, 6 ]``
800 |
801 | #### Other versions
802 | - ``skipf(field, count)``
803 | - ``skipff(source, destination, count)``
804 |
805 | [Back to Index](#methodsindex)
806 |
807 |
808 | ### take(count)
809 |
810 | Creates a new array that contains only the first specified number of items.
811 |
812 | ``` javascript
813 | trans([ 1, 2, 3, 4, 5, 6 ]).take(2).value();
814 | ```
815 | => ``[ 1, 2 ]``
816 |
817 | #### Other versions
818 | - ``takef(field, count)``
819 | - ``takeff(source, destination, count)``
820 |
821 | [Back to Index](#methodsindex)
822 |
823 |
824 | ### first()
825 |
826 | Replaces the target array with its first element.
827 |
828 | ``` javascript
829 | trans([ 1, 2, 4 ]).first().value();
830 | ```
831 | =>``1``
832 |
833 | ``` javascript
834 | trans([]).first().value();
835 | ```
836 | => ``null``
837 |
838 | #### Other versions
839 | - ``firstf(field)``
840 | - ``firstff(source, destination)``
841 |
842 | [Back to Index](#methodsindex)
843 |
844 |
845 | ### last()
846 |
847 | Replaces the target array with its last element.
848 |
849 | ``` javascript
850 | trans([ 1, 2, 4 ]).last().value();
851 | ```
852 | =>``4``
853 |
854 | #### Other versions
855 | - ``lastf(field)``
856 | - ``lastff(source, destination)``
857 |
858 | [Back to Index](#methodsindex)
859 |
860 |
861 | ### uniq(uniqField, *transformers)
862 |
863 | Removes duplicate items from an array according to the value at the specified field
864 | (possibly transformed).
865 |
866 | ``` javascript
867 | trans([ 1, 1, 2 ]).uniq().value();
868 | ```
869 | => ``[ 1, 2 ]``
870 |
871 | ``` javascript
872 | trans([ 1, 11, 12, 22 ]).uniq(null, [ mod, 10 ]).value();
873 | ```
874 | => ``[ 1, 12 ]``
875 |
876 | ``` javascript
877 | trans([ { a: 'abc' }, { a: 'aab' }, { a: 'bcd' }, { a: 'bad' } ])
878 | .uniq('a', [ 'charAt', 0 ])
879 | .value();
880 | ```
881 | => ``[ { a: 'abc' }, { a: 'bcd' } ]``
882 |
883 | #### Other versions
884 | - ``uniqf(field, uniqField, *transformers)``
885 | - ``uniqff(source, destination, uniqField, *transformers)``
886 |
887 | [Back to Index](#methodsindex)
888 |
889 | ## Gotchas and Limitations
890 |
891 | Some transformations will modify the original data while others won't. See the two
892 | examples below.
893 |
894 | ``` javascript
895 | var a = [ 1, 2, 3 ];
896 | var t = trans(a).map('shift').value();
897 |
898 | console.log(a);
899 | console.log(t);
900 | ```
901 | => ``[ 2, 3 ]``
902 | => ``1``
903 |
904 | ``` javascript
905 | var a = [ 1, 2, 3 ];
906 | var t = trans(a).map(['slice', 0, 1], 'shift').value();
907 |
908 | console.log(a);
909 | console.log(t);
910 | ```
911 | => ``[ 1, 2, 3 ]``
912 | => ``1``
913 |
914 | Calling ``mapff`` without any transformer functions will just create another reference
915 | to the same object. This may lead to unexpected results.
916 |
917 | ``` javascript
918 | var obj = { a: { b: 2, c: 'X' } };
919 | var res = trans(obj).mapff('a', 'c').value();
920 |
921 | console.log(res);
922 |
923 | res.a.c = 'changed';
924 | console.log(res);
925 | ```
926 |
927 | => ``{ a: { b: 2, c: 'X' }, c: { b: 2, c: 'X' } }``
928 | => ``{ a: { b: 2, c: 'changed' }, c: { b: 2, c: 'changed' } }``
929 |
930 | Calling ``mapff`` without any transformer functions and setting the destination to
931 | be a field on the source object would create a circular reference.
932 |
933 | ``` javascript
934 | trans({ a: { b: { c: 1 } } }).mapff('a', 'a.b.d').value();
935 | ```
936 | => ``{ a: { b: { c: 1, d: [Circular] } } }``
937 |
938 | ## License
939 |
940 | MIT
941 |
--------------------------------------------------------------------------------
/test/trans/map-test.js:
--------------------------------------------------------------------------------
1 | module.exports = function (util) {
2 | var trans = util.trans
3 | , assert = util.assert
4 | , add = util.add
5 | , sum = util.sum
6 | , square = util.square;
7 |
8 | describe('map', function () {
9 | it('should map the transformation state - object1', function () {
10 | var o = 'abc'
11 | , t = trans(o).map('toUpperCase').value();
12 | assert.strictEqual(t, 'ABC');
13 | });
14 |
15 | it('should map the transformation state - object2', function () {
16 | var o = { a: 'abc' }
17 | , t = trans(o).map('a', 'toUpperCase').value();
18 | assert.strictEqual(t, 'ABC');
19 | });
20 |
21 | it('should map the transformation state - object3', function () {
22 | var o = { a: { b: 'abc' } }
23 | , t = trans(o).map('a', 'b', ['substring', 1, 3], 'toUpperCase').value();
24 | assert.strictEqual(t, 'BC');
25 | });
26 |
27 | it('should map the transformation state - object3', function () {
28 | var o = { a: { b: { c: 1 } } }
29 | , t = trans(o).map('a', 'b', 'c').value();
30 | assert.strictEqual(t, 1);
31 | });
32 |
33 | it('should map the transformation state - array1a', function () {
34 | var o = [ 1, 2, 3 ]
35 | , t = trans(o).map('.', [add, 1]).value();
36 | assert.deepEqual(t, [ 2, 3, 4 ]);
37 | });
38 |
39 | it('should accept functions in array format1', function () {
40 | var o = [ 1, 2, 3 ]
41 | , t = trans(o).map('.', [add, 5]).value();
42 | assert.deepEqual(t, [ 6, 7, 8 ]);
43 | });
44 |
45 | it('should pass in the element index when iterating through an array', function () {
46 | var o = [ 'a', 'b', 'c', 'd' ]
47 | , t = trans(o).map('.', function (x) { return x + this.getIndex(); }).value();
48 | assert.deepEqual(t, [ 'a0', 'b1', 'c2', 'd3' ]);
49 | });
50 |
51 | it('should accept functions in array format2', function () {
52 | var o = [ 'ab', 'cde' ]
53 | , t = trans(o).map('.', ['concat', 'ww', 'zz']).value();
54 | assert.deepEqual(t, [ 'abwwzz', 'cdewwzz' ]);
55 | });
56 |
57 | it('should map the transformation state - array 1', function () {
58 | var o = [ { b: 1, c: 3 }, { b: 2, c: 3 } ]
59 | , t = trans(o).map('.', function(x) { return x.b + x.c; }).value();
60 | assert.deepEqual(t, [ 4, 5 ] );
61 | });
62 |
63 | it('should map the transformation state - array1b', function () {
64 | var o = [ { a: 1 }, { a: 2 }, {a: 3 } ]
65 | , t = trans(o).map('.', 'a', [add, 1]).value();
66 | assert.deepEqual(t, [ 2, 3, 4 ]);
67 | });
68 |
69 | it('should map the transformation state - array1c', function () {
70 | var o = [ { a: { b: 1 } }, { a: { b: 2 } }, {a: { b: 3 } } ]
71 | , t = trans(o).map('.', 'a', 'b', [add, 1]).value();
72 | assert.deepEqual(t, [ 2, 3, 4 ]);
73 | });
74 |
75 | it('should map the transformation state - array1d', function () {
76 | var o = [
77 | { a: [ { b: 1 }, { b: 2 } ] }
78 | , { a: [ { b: 2 }, { b: 2 } ] }
79 | , { a: [ { b: 1 }, { b: 3 } ] }
80 | , { a: [ { b: 3 }, { b: 4 } ] } ]
81 | , t = trans(o).map('.', 'a', '.', 'b', [add, 5]).value();
82 | assert.deepEqual(t, [ [ 6, 7 ], [ 7, 7 ], [ 6, 8 ], [ 8, 9 ] ]);
83 | });
84 |
85 | it('should map the transformation state - array1e', function () {
86 | var o = [
87 | { a: [ { b: 1 }, { b: 2 } ] }
88 | , { a: [ { b: 2 }, { b: 2 } ] }
89 | , { a: [ { b: 1 }, { b: 3 } ] }
90 | , { a: [ { b: 3 }, { b: 4 } ] } ]
91 | , t = trans(o).map('.', 'a').value();
92 | assert.deepEqual(t, [
93 | [ { b: 1 }, { b: 2 } ]
94 | , [ { b: 2 }, { b: 2 } ]
95 | , [ { b: 1 }, { b: 3 } ]
96 | , [ { b: 3 }, { b: 4 } ] ]);
97 | });
98 |
99 | it('should map the transformation state - array2', function () {
100 | var o = [ 'abc', 'de', 'defg' ]
101 | , t = trans(o).map('length').value();
102 | assert.deepEqual(t, 3);
103 | });
104 |
105 | it('should map the transformation state - array2', function () {
106 | var o = [ 'abc', 'de', 'defg' ]
107 | , t = trans(o).map('length').value();
108 | assert.deepEqual(t, 3);
109 | });
110 |
111 | it('should map the transformation state - array3', function () {
112 | var o = [ 'abc', 'de', 'defg' ]
113 | , t = trans(o).map('.', 'length').value();
114 | assert.deepEqual(t, [ 3, 2, 4 ]);
115 | });
116 |
117 | it('should apply multiple transformers in the given order', function () {
118 | var o = [ 1, 2, 3 ]
119 | , e1 = trans(o).map('.', square, [add, 1]).value()
120 | , e2 = trans(o).map('.', [add, 1], square).value();
121 | assert.deepEqual(e1, [ 2, 5, 10 ]);
122 | assert.deepEqual(e2, [ 4, 9, 16 ]);
123 | });
124 |
125 | it('should allow passing field names for transformers', function () {
126 | var o = [ 1, 2, 3 ]
127 | , t = trans(o).map('length', [add, 2]).value();
128 | assert.strictEqual(t, o.length + 2);
129 | });
130 |
131 | it('should work with transformers that return null', function () {
132 | var o = 'abc'
133 | , t = trans(o).map(function () { return null; }).value();
134 | assert.strictEqual(t, null);
135 | });
136 |
137 | it('should collect values from nested arrays', function () {
138 | var o = [
139 | [ { a: { b: 1 } }, { a: { b: 2 } } ]
140 | , [ { a: { b: 3 } } ]
141 | , [ { a: { b: 4 } }, { a: { b: 5 } } ] ]
142 | , t = trans(o).map('.', '.', 'a','b', [add, 5]);
143 | assert.deepEqual(t.value(), [ [ 6, 7 ], [ 8 ], [ 9, 10 ] ]);
144 | assert.deepEqual(t.map('.', sum).value(), [ 13, 8, 19 ]);
145 | assert.deepEqual(t.map(sum).value(), 40);
146 | });
147 |
148 | it('should allow passing hash maps as transformers', function () {
149 | var o = [ 1, 2, 3, 4 ]
150 | , nums = { 1: 'one', 2: 'two', 3: 'three', 4: 'four' }
151 | , t = trans(o).map('.', nums).value();
152 | assert.deepEqual(t, [ 'one', 'two', 'three', 'four' ]);
153 | });
154 |
155 | it('should fail if a null transformer is specified', function () {
156 | assert.throws(function () {
157 | trans({ a: 1 }).map(null);
158 | }, /could not apply transformer null/i);
159 | });
160 |
161 | it('should fail if an invalid transformer is specified', function () {
162 | assert.throws(function () {
163 | trans({ a: 1 }).map(1);
164 | }, /could not apply transformer 1/i);
165 | });
166 | });
167 |
168 | describe('mapf', function () {
169 | it('should map the object at the given field - object', function () {
170 | var o = { a: 1 }
171 | , t = trans(o).mapf('a', [add, 1]).value();
172 | assert.deepEqual(t, { a: 2 });
173 | });
174 |
175 | it('should behave like map if the specified field is null', function () {
176 | var o = [ 1, 2 ]
177 | , t = trans(o).mapf(null, 'length').value();
178 | assert.strictEqual(t, 2);
179 | });
180 |
181 | it('should map the object at the given field - array1a', function () {
182 | var o = { a: { b: [ 1, 2, 3 ] } }
183 | , t = trans(o).mapf('a.b.', square, [add, 1]).value();
184 | assert.deepEqual(t, { a: { b: [ 2, 5, 10 ] } });
185 | });
186 |
187 | it('should map the object at the given field - array1b', function () {
188 | var o = { a: { b: [ 1, 2, 3 ] } }
189 | , t = trans(o).mapf('a.b', 'length', [add, 1]).value();
190 | assert.deepEqual(t, { a: { b: 4 } });
191 | });
192 |
193 | it('should replace an array with its first element', function () {
194 | var o = { a: { b: [ 1, 2, 3 ] } }
195 | , t = trans(o).mapf('a.b', 'shift').value();
196 | assert.deepEqual(t, { a: { b: 1 } });
197 | });
198 |
199 | it('should replace an array with its last element', function () {
200 | var o = { a: { b: [ 1, 2, 3 ] } }
201 | , t = trans(o).mapf('a.b', 'pop').value();
202 | assert.deepEqual(t, { a: { b: 3 } });
203 | });
204 |
205 | it('should take only the first x elements from an array', function () {
206 | var o = { a: { b: [ 1, 2, 3, 4, 4, 5, 8 ] } }
207 | , t = trans(o).mapf('a.b', ['slice', 0, 3]).value();
208 | assert.deepEqual(t, { a: { b: [ 1, 2, 3 ] } });
209 | });
210 |
211 | it('should map the object at the given field - array2', function () {
212 | var o = { a: [ { b: 1 }, { b: 2 }, { b: 3 } ] }
213 | , t = trans(o).mapf('a.b', [add, 1], square).value();
214 | assert.deepEqual(t, { a: [ { b: 4 }, { b: 9 }, { b: 16 } ] });
215 | });
216 |
217 | it('should map the object at the given field - array3', function () {
218 | var o = { a: [ { b: { c: 1 } }, { b: { c: 2 } }, { b: { c: 3 } } ] }
219 | , t = trans(o).mapf('a.b.c', [add, 1], square).value();
220 | assert.deepEqual(t, { a: [ { b: { c: 4 } }, { b: { c: 9 } }, { b: { c: 16 } } ] });
221 | });
222 |
223 | it('should map the object at the given field - array4', function () {
224 | var o = [ { a: { b: { c: 'abc' } } }, { a: { b: { c: 'def' } } } ]
225 | , t = trans(o).mapf('a.b.c', 'toUpperCase').value();
226 | assert.deepEqual(t, [ { a: { b: { c: 'ABC' } } }, { a: { b: { c: 'DEF' } } } ]);
227 | });
228 |
229 | it('should map the object at the given field - array5', function () {
230 | var o = [
231 | { a: [ { b: { c: 1 } }, { b: { c: 2 } } ] }
232 | , { a: [ { b: { c: 2 } }, { b: { c: 3 } } ] } ]
233 | , t = trans(o).mapf('a.b.c', [add, 1]).value();
234 |
235 | assert.deepEqual(t, [
236 | { a: [ { b: { c: 2 } }, { b: { c: 3 } } ] }
237 | , { a: [ { b: { c: 3 } }, { b: { c: 4 } } ] } ]);
238 | });
239 |
240 | it('should map the object at the given field - array6', function () {
241 | var o = { a: [ { b: [ 1, 1, 2 ] }, { b: [ 3, 3 ] }, { b: [ 1, 2, 3 ] } ] }
242 | , t = trans(o).mapf('a.b.', [add, 1]).value();
243 | assert.deepEqual(t, { a: [ { b: [ 2, 2, 3 ] }, { b: [ 4, 4 ] }, { b: [ 2, 3, 4 ] } ] });
244 | });
245 |
246 | it('should map the object at the given field - array7', function () {
247 | var o = { a: [ { b: [ 1, 1, 2 ] }, { b: [ 3, 3 ] }, { b: [ 1, 2, 3 ] } ] }
248 | , t = trans(o).mapf('a.b', 'length').value();
249 | assert.deepEqual(t, { a: [ { b: 3 }, { b: 2 }, { b: 3 } ] });
250 | });
251 |
252 | it('should create missing fields 1', function () {
253 | var o = [ { a: { b: 1 } }, { a: { } }, { a: { b: 3 } } ]
254 | , t = trans(o).mapf('a.b', [add, 1]).value();
255 | assert.deepEqual(t, [ { a: { b: 2 } }, { a: { b: 1 } }, { a: { b: 4 } } ]);
256 | });
257 |
258 | it('should create missing fields 2', function () {
259 | var o = { a: { b: {} } }
260 | , t = trans(o).mapf('a.b.c', [add, 1]).value();
261 | assert.deepEqual(t, { a: { b: { c: 1 } } });
262 | });
263 |
264 | it('should work with nested fields', function () {
265 | var o = { a: { b: { c: 'abc' } } }
266 | , t = trans(o).mapf('a.b.c', 'toUpperCase').value();
267 | assert.deepEqual(t, { a: { b: { c: 'ABC' } } });
268 | });
269 |
270 | it('should work with nested arrays', function () {
271 | var o = { a: [ [ { b: 1 } ], [ { b: 2 } ] ] }
272 | , t = trans(o).mapf('a.b', [add, 1]).value();
273 | assert.deepEqual(t, { a: [ [ { b: 2 } ], [ { b: 3 } ] ] });
274 | });
275 |
276 | it('should handle empty objects', function () {
277 | var o = [ {}, {} ]
278 | , t = trans(o).mapf('b', [ add, 1 ]).value();
279 | assert.deepEqual(t, [ { b: 1 }, { b: 1 } ]);
280 | });
281 |
282 | it('should pass the index when iterating arrays 1', function () {
283 | var o = { a: [
284 | { b: [ 'a', 'b', 'c' ] }
285 | , { b: [ 'd', 'e' ] }
286 | , { b: [ 'f' ] } ] }
287 | , t = trans(o).mapf('a.b.', function(x) { return x + this.getIndex(); }).value();
288 | assert.deepEqual(t, { a: [
289 | { b: [ 'a0', 'b1', 'c2' ] }
290 | , { b: [ 'd0', 'e1' ] }
291 | , { b: [ 'f0' ] } ] });
292 | });
293 |
294 | it('should pass the index when iterating arrays 2', function () {
295 | var o = [
296 | { a: [ { b: [ 'aa', 'ab' ] }, { b: [ 'ac', 'ad', 'ae' ] } ] }
297 | , { a: [ { b: [ 'ba', 'bb' ] }, { b: [ 'bc', 'bd', 'be' ] } ] }
298 | , { a: [ { b: [ 'ca', 'cb' ] }, { b: [ 'cc', 'cd', 'ce' ] } ] }
299 | , { a: [ { b: [ 'da', 'db' ] }, { b: [ 'dc', 'dd', 'de' ] } ] } ]
300 | , t = trans(o)
301 | .map('.', 'a', '.', 'b', '.', function (x) { return x + this.getIndex(); })
302 | .value();
303 | assert.deepEqual(t, [
304 | [ [ 'aa0', 'ab1' ], [ 'ac0', 'ad1', 'ae2' ] ]
305 | , [ [ 'ba0', 'bb1' ], [ 'bc0', 'bd1', 'be2' ] ]
306 | , [ [ 'ca0', 'cb1' ], [ 'cc0', 'cd1', 'ce2' ] ]
307 | , [ [ 'da0', 'db1' ], [ 'dc0', 'dd1', 'de2' ] ]
308 | ]);
309 | });
310 | });
311 |
312 | describe('mapff', function () {
313 | it('should map the object at the given field - object', function () {
314 | var o = { a: 1 }
315 | , t = trans(o).mapff('a', 'c', [add, 1]).value();
316 | assert.deepEqual(t, { a: 1, c: 2 });
317 | });
318 |
319 | it('should iterate the array when there is a final dot in the field name', function () {
320 | var o = { a: { b: [ 1, 2, 3 ] } }
321 | , t = trans(o).mapff('a.b.', 'c', square, [add, 1]).value();
322 | assert.deepEqual(t, { a: { b: [ 1, 2, 3 ] }, c: [ 2, 5, 10 ] });
323 | });
324 |
325 | it('should not iterate the array when there is no final dot in the field name', function () {
326 | var o = { a: { b: [ 1, 2, 3 ] } }
327 | , t = trans(o).mapff('a.b', 'c', 'length', [add, 1], square).value();
328 | assert.deepEqual(t, { a: { b: [ 1, 2, 3 ] }, c: 16 });
329 | });
330 |
331 | it('should map the object at the given field - array1c', function () {
332 | var o = { a: { b: [ 1, 2, 3 ] } }
333 | , t = trans(o).mapff('a.b.', 'a.b', square, [add, 1]).value();
334 | assert.deepEqual(t, { a: { b: [ 2, 5, 10 ] } });
335 | });
336 |
337 | it('should map the object at the given field - array1d', function () {
338 | var o = { a: { b: [ 1, 2, 3 ] } }
339 | , t = trans(o).mapff('a.b', 'a.b', sum).value();
340 | assert.deepEqual(t, { a: { b: 6 } });
341 | });
342 |
343 | it('should map the object at the given field - array2a', function () {
344 | var o = { a: [ { b: 1 }, { b: 2 }, { b: 3 } ] }
345 | , t = trans(o).mapff('a.b', 'a.c', [add, 1], square).value();
346 | assert.deepEqual(t, { a: [ { b: 1, c: 4 } , { b: 2, c: 9 } , { b: 3, c: 16 } ] });
347 | });
348 |
349 | it('should map the object at the given field - array2b', function () {
350 | var o = { a: [ { b: 1 }, { b: 2 }, { b: 3 } ] }
351 | , t = trans(o).mapff('a.b', 'c', '.', [add, 1], square).value();
352 | assert.deepEqual(t, { a: [ { b: 1 }, { b: 2 }, { b: 3 } ] , c: [ 4, 9, 16 ] });
353 | });
354 |
355 | it('should map the object at the given field - array2c', function () {
356 | var o = { a: [ { b: 1 }, { b: 2 }, { b: 3 } ] }
357 | , t = trans(o).mapff('a', 'c').value();
358 | assert.deepEqual(t, {
359 | a: [ { b: 1 }, { b: 2 }, { b: 3 } ]
360 | , c: [ { b: 1 }, { b: 2 }, { b: 3 } ] });
361 | });
362 |
363 | it('should map the correct source to the correct destination', function () {
364 | var o = { a: [ { b: 1 }, { b: 2 }, { b: 3 } ], e: [ { b: 1 }, { b: 2 } ] }
365 | , t = trans(o).mapff('a.b', 'e.c').value();
366 | assert.deepEqual(t, {
367 | a: [ { b: 1 }, { b: 2 }, { b: 3 } ]
368 | , e: [ { b: 1, c: [ 1, 2, 3 ] }, { b: 2, c: [ 1, 2, 3 ] } ]
369 | });
370 | });
371 |
372 | it('should map the correct source to the correct destination - nested arrays', function () {
373 | var o = { a: [ { b: 1 }, { b: 2 }, { b: 3 } ], e: [ [ { b: 1 } ], [ { b: 2 } ] ] }
374 | , t = trans(o).mapff('a.b', 'e.c').value();
375 | assert.deepEqual(t, {
376 | a: [ { b: 1 }, { b: 2 }, { b: 3 } ]
377 | , e: [ [ { b: 1, c: [ 1, 2, 3 ] } ], [ { b: 2, c: [ 1, 2, 3 ] } ] ]
378 | });
379 | });
380 |
381 | it('should create a field on the target object(s)', function () {
382 | var o = { a: [ { b: 1 }, { b: 2 }, { b: 3 } ] }
383 | , t = trans(o).mapff('a.c', 'a.c', function () { return 'a'; }).value();
384 | assert.deepEqual(t, { a: [ { b: 1, c: 'a' } , { b: 2, c: 'a' } , { b: 3, c: 'a' } ] });
385 | });
386 |
387 | it('should work with nested fields within an array', function () {
388 | var o = { a: [ { b: { c: 1 } }, { b: { c: 2 } }, { b: { c: 3 } } ] }
389 | , t = trans(o).mapff('a.b.c', 'd', '.', [add, 1], square).value();
390 | assert.deepEqual(t, {
391 | a: [ { b: { c: 1 } }, { b: { c: 2 } }, { b: { c: 3 } } ]
392 | , d: [ 4, 9, 16 ]
393 | });
394 | });
395 |
396 | it('should replace nested fields within arrays 1', function () {
397 | var o = { a: [ { b: { c: 1 } }, { b: { c: 2 } }, { b: { c: 3 } } ] }
398 | , t = trans(o).mapff('a.b.c', 'a.b', [add, 1], square).value();
399 | assert.deepEqual(t, { a: [ { b: 4 }, { b: 9 }, { b: 16 } ] });
400 | });
401 |
402 | it('should replace nested fields within arrays 2', function () {
403 | var o = [ { a: { b: { c: 'abc' } } }, { a: { b: { c: 'def' } } } ]
404 | , t = trans(o).mapff('a.b.c', 'a', 'toUpperCase').value();
405 | assert.deepEqual(t, [ { a: 'ABC' }, { a: 'DEF' } ]);
406 | });
407 |
408 | it('should map fields within the proper object 1', function () {
409 | var o = { a: [ { b: { c: 1 } }, { b: { c: 2 } }, { b: { c: 3 } } ] }
410 | , t = trans(o).mapff('a.b.c', 'a.b.d', [add, 1], square).value();
411 | assert.deepEqual(t, { a: [
412 | { b: { c: 1, d: 4 } }
413 | , { b: { c: 2, d: 9 } }
414 | , { b: { c: 3, d: 16 } } ] });
415 | });
416 |
417 | it('should map fields within the proper object 2', function () {
418 | var o = [
419 | { a: { b: { c: 'abc' } } }
420 | , { a: { b: { c: 'def' } } } ]
421 | , t = trans(o).mapff('a.b.c', 'd', 'toUpperCase').value();
422 | assert.deepEqual(t, [
423 | { a: { b: { c: 'abc' } }, d: 'ABC' }
424 | , { a: { b: { c: 'def' } }, d: 'DEF' }
425 | ]);
426 | });
427 |
428 | it('should map fields within the proper object 3', function () {
429 | var o = [
430 | { a: [ { b: { c: 1 } }, { b: { c: 2 } } ] }
431 | , { a: [ { b: { c: 2 } }, { b: { c: 3 } } ] } ]
432 | , t = trans(o).mapff('a.b.c', 'd', '.', [add, 1], square).value();
433 | assert.deepEqual(t, [
434 | { a: [ { b: { c: 1 } }, { b: { c: 2 } } ], d: [ 4, 9 ] }
435 | , { a: [ { b: { c: 2 } }, { b: { c: 3 } } ], d: [ 9, 16 ] } ]);
436 | });
437 |
438 | it('should map fields within the proper object 4', function () {
439 | var o = [
440 | { a: [ { b: { c: 1 } }, { b: { c: 2 } } ] }
441 | , { a: [ { b: { c: 2 } }, { b: { c: 3 } } ] } ]
442 | , t = trans(o).mapff('a.b.c', 'a.b', [add, 1], square).value();
443 | assert.deepEqual(t, [
444 | { a: [ { b: 4 }, { b: 9 } ] }
445 | , { a: [ { b: 9 }, { b: 16 } ] } ]);
446 | });
447 |
448 | it('should map fields within the proper object 5', function () {
449 | var o = [
450 | { a: [ { b: { c: 1 } }, { b: { c: 2 } } ] }
451 | , { a: [ { b: { c: 2 } }, { b: { c: 3 } } ] } ]
452 | , t = trans(o).mapff('a.b.c', 'a', '.', [add, 1], square).value();
453 | assert.deepEqual(t, [
454 | { a: [ 4, 9 ] }
455 | , { a: [ 9, 16 ] } ]);
456 | });
457 |
458 | it('should map fields within the proper object 6', function () {
459 | var o = [
460 | { a: [ { b: { c: 1 } }, { b: { c: 2 } } ] }
461 | , { a: [ { b: { c: 2 } }, { b: { c: 3 } } ] } ]
462 | , t = trans(o).mapff('a.b.c', 'a.b.d', [add, 1], square).value();
463 | assert.deepEqual(t, [
464 | { a: [ { b: { c: 1, d: 4 } }, { b: { c: 2, d: 9 } } ] }
465 | , { a: [ { b: { c: 2, d: 9 } }, { b: { c: 3, d: 16 } } ] } ]);
466 | });
467 |
468 | it('should map fields within the proper object 7', function () {
469 | var o = [ { a: [ { b: { c: 1 } }, { b: { c: 2 } } ] }, { a: [ { b: { c: 3 } } ] } ]
470 | , t = trans(o).mapff('a.b.c', 'e', sum).value();
471 | assert.deepEqual(t, [
472 | { a: [ { b: { c: 1 } }, { b: { c: 2 } } ], e: 3 }
473 | , { a: [ { b: { c: 3 } } ], e: 3 } ]);
474 | });
475 |
476 | it('should not skip missing fields 1', function () {
477 | var o = [ { a: { b: 1 } }, { a: { } }, { a: { b: 3 } } ]
478 | , t = trans(o).mapff('a.b', 'c', [add, 1]).value();
479 | assert.deepEqual(t, [
480 | { a: { b: 1 }, c: 2 }
481 | , { a: { }, c: 1 }
482 | , { a: { b: 3 }, c: 4 }
483 | ]);
484 | });
485 |
486 | it('should not skip missing fields 2', function () {
487 | var o = { a: { b: {} } }
488 | , t = trans(o).mapff('a.b.c', 'd', [add, 1]).value();
489 | assert.deepEqual(t, { a: { b: {} }, d: 1 });
490 | });
491 |
492 | it('should not skip missing fields 3', function () {
493 | var o = [ { a: { b: 1 } }, { a: { b: 2 } }, { a: {} }, { a: {} } ]
494 | , t = trans(o)
495 | .mapff('a.b', 'a.b', function (x) { return x || 100; })
496 | .value();
497 | assert.deepEqual(t, [
498 | { a: { b: 1 } }
499 | , { a: { b: 2 } }
500 | , { a: { b: 100 } }
501 | , { a: { b: 100 } } ]);
502 | });
503 |
504 | it('should allow nested fields', function () {
505 | var o = { a: { b: { c: 'abcde' } } }
506 | , t = trans(o)
507 | .mapff('a.b.c', 'a.d', 'toUpperCase', util.truncate)
508 | .value();
509 | assert.deepEqual(t, { a: { b: { c: 'abcde' }, d: 'ABC' } });
510 | });
511 |
512 | it('should override fields with the same name 1', function () {
513 | var o = { a: { b: 3 } }
514 | , t = trans(o).mapff('a.b', 'a').value();
515 | assert.deepEqual(t, { a: 3 });
516 | });
517 |
518 | it('should override fields with the same name 2', function () {
519 | var o = {
520 | a: [ { b: 1 }, { b: 2 }, { b: 3 } ]
521 | , e: [ { b: 1, c: 'a' }, { b: 2, c: 'b' } ] }
522 | , t = trans(o).mapff('a.b', 'e.c').value();
523 | assert.deepEqual(t, {
524 | a: [ { b: 1 }, { b: 2 }, { b: 3 } ]
525 | , e: [ { b: 1, c: [ 1, 2, 3 ] }, { b: 2, c: [ 1, 2, 3 ] } ]
526 | });
527 | });
528 |
529 | it('should override fields with the same name 3', function () {
530 | var o = {
531 | a: [ { b: '1' }, { b: '2' }, { b: '3' } ]
532 | , e: [ { b: 1, c: 'a' }, { b: 2, c: 'b' } ] }
533 | , t = trans(o).mapff('a.b', 'e.c', '.', function (x) { return x + ':' + this.getIndex(); }).value();
534 | assert.deepEqual(t, {
535 | a: [ { b: '1' }, { b: '2' }, { b: '3' } ]
536 | , e: [
537 | { b: 1, c: [ '1:0', '2:1', '3:2' ] }
538 | , { b: 2, c: [ '1:0', '2:1', '3:2' ] }
539 | ]
540 | });
541 | });
542 |
543 | it('should work with nested arrays 1', function () {
544 | var o = [ [ { a: { b: 1 } }, { a: { b: 2 } } ], [ { a: { b: 3 } } ] ]
545 | , t = trans(o).mapff('a.b', 'a.c', [add, 2]).value();
546 | assert.deepEqual(t, [
547 | [ { a: { b: 1, c: 3 } }, { a: { b: 2, c: 4 } } ]
548 | , [ { a: { b: 3, c: 5 } } ]
549 | ]);
550 | });
551 |
552 | it('should work with nested arrays 2', function () {
553 | var o = [ [ { a: { b: 1 } }, { a: { b: 2 } } ], [ { a: { b: 3 } } ] ]
554 | , t = trans(o).mapff('a.b', 'c', [add, 2]).value();
555 | assert.deepEqual(t, [
556 | [ { a: { b: 1 }, c: 3 }, { a: { b: 2 }, c: 4 } ]
557 | , [ { a: { b: 3 }, c: 5 } ]
558 | ]);
559 | });
560 |
561 | it('should make all array indexes available', function () {
562 | var o = [
563 | { a: { b: [ { c: [ 'a', 'b' ] }, { c: [ 'c', 'd', 'e' ] } ] } }
564 | , { a: { b: [ { c: [ 'f', 'g', 'k' ] }, { c: [ 'l', 'm', 'n', 'o' ] }, { c: [ 'r', 's' ] } ] } }
565 | , { a: { b: [ { c: [ 't', 'u', 'v', 'w' ] } ] } }
566 | ]
567 | , t = trans(o).mapff('a.b.c.', null, '.', function (x) {
568 | return x + ':' + this.getIndexes() + ':' + this.getIndex();
569 | }).value();
570 | assert.deepEqual(t, [
571 | [ [ 'a:0,0,0:0', 'b:1,0,0:1' ], [ 'c:0,1,0:0', 'd:1,1,0:1', 'e:2,1,0:2' ] ]
572 | , [
573 | [ 'f:0,0,1:0', 'g:1,0,1:1', 'k:2,0,1:2' ]
574 | , [ 'l:0,1,1:0', 'm:1,1,1:1', 'n:2,1,1:2', 'o:3,1,1:3' ]
575 | , [ 'r:0,2,1:0', 's:1,2,1:1' ]
576 | ]
577 | , [ [ 't:0,0,2:0', 'u:1,0,2:1', 'v:2,0,2:2', 'w:3,0,2:3' ] ] ]);
578 | });
579 |
580 | it('should parse field paths properly', function () {
581 | var o = [ { names: { taught: [ 1, 2, 4 ] } }, { names: { taught: [ 1 ] } } ]
582 | , t = trans(o).mapff('names.taught', 'names.taughtCount', 'length').value();
583 | assert.deepEqual(t, [
584 | { names: { taught: [ 1, 2, 4 ], taughtCount: 3 } }
585 | , { names: { taught: [ 1 ], taughtCount: 1 } }
586 | ]);
587 | });
588 |
589 | it('should replace the whole object when the destination field is null 1', function () {
590 | var o = [ 1, 2, 3 ]
591 | , t = trans(o).mapff(null, null, 'length').value();
592 | assert.strictEqual(t, 3);
593 | });
594 |
595 | it('should replace the whole object when the destination field is null 2', function () {
596 | var o = [ 1, 2, 3 ]
597 | , t = trans(o).mapff('.', null, [add, 1]).value();
598 | assert.deepEqual(t, [2, 3, 4]);
599 | });
600 |
601 | it('should replace the whole object when the destination field is null 3', function () {
602 | var o = [ { a: { b: [ 1, 2 ] } }, { a: { b: [ 4 ] } }, { a: { b: [ 6, 7 ,8 ] } } ]
603 | , t = trans(o).mapff('a.b').value();
604 | assert.deepEqual(t, [ [ 1, 2 ], [ 4 ], [ 6, 7, 8 ] ]);
605 | });
606 |
607 | it('should replace the whole object when the destination field is null 3b', function () {
608 | var o = [ { a: { b: [ 'a', 'b' ] } }, { a: { b: [ 'c' ] } }, { a: { b: [ 'd', 'e' ,'f' ] } } ]
609 | , t = trans(o).mapff('a.b.', null, function (x) { return x + this.getIndex(); }).value();
610 | assert.deepEqual(t, [ [ 'a0', 'b1' ], [ 'c0' ], [ 'd0', 'e1', 'f2' ] ]);
611 | });
612 |
613 | it('should replace the whole object when the destination field is null 4', function () {
614 | var o = [
615 | { a: [ { b: [ 1, 2 ] }, { b: [ 3, 4 ] } ] }
616 | , { a: [ { b: [ 4 ] }, { b: [ 11, 22 ] }, { b: [ 199 ] } ] }
617 | , { a: [ { b: [ 6, 7 ,8 ] } ] } ]
618 | , t = trans(o).mapff('a.b').value();
619 | assert.deepEqual(t, [
620 | [ [ 1, 2 ], [ 3, 4 ] ]
621 | , [ [ 4 ], [ 11, 22 ], [ 199 ] ]
622 | , [ [ 6, 7, 8 ] ]
623 | ]);
624 | });
625 |
626 | it('should handle falsy values 1', function () {
627 | var o = { a: 0 }
628 | , t = trans(o).mapff('a', 'b', square).value();
629 | assert.deepEqual(t, { a: 0, b: 0 });
630 | });
631 |
632 | it('should handle falsy values 2', function () {
633 | var o = { a: { b: 0 } }
634 | , t = trans(o).mapff('a.b', 'a.c', square).value();
635 | assert.deepEqual(t, { a: { b: 0, c: 0 }});
636 | });
637 |
638 | it('should handle falsy values 3', function () {
639 | var t = trans(0).mapff(null, null, square).value();
640 | assert.strictEqual(t, 0);
641 | });
642 |
643 | it('should handle empty arrays', function () {
644 | var t = trans([]).mapff('a.b', 'a.c', [ add, 1 ]).value();
645 | assert.deepEqual(t, []);
646 | });
647 |
648 | it('should handle empty objects', function () {
649 | var o = [ {}, {} ]
650 | , t = trans(o).mapff('a', 'b', [ add, 1 ]).value();
651 | assert.deepEqual(t, [ { b: 1 }, { b: 1 } ]);
652 | });
653 |
654 | it('should replace the whole object when the destination field is null 5', function () {
655 | var o = [ { a: { b: 1 } }, { a: { b: 2 } }, { a: {} }, { a: {} } ]
656 | , t = trans(o)
657 | .mapff('a.b', null, function (x) { return x || 100; }, [add, 2])
658 | .value();
659 | assert.deepEqual(t, [ 3, 4, 102, 102 ]);
660 | });
661 |
662 | it('should allow setting a field on the source object 1', function () {
663 | var o = { a: { b: 1, c: 2 } }
664 | , t = trans(o).mapff('a', 'a.d', function (x) { return x.b + x.c; }).value();
665 | assert.deepEqual(t, { a: { b: 1, c: 2, d: 3 } });
666 | });
667 |
668 | it('should allow setting a field on the source object 2', function () {
669 | var o = { a: [ { b: 1 }, { b: 2 } ] }
670 | , t = trans(o).mapff('a', 'a.c', 'b', [ add, 1 ]).value();
671 | assert.deepEqual(t, { a: [ { b: 1, c: 2 }, { b: 2, c: 3 } ] });
672 | });
673 |
674 | it('should allow setting a field on the source object 3', function () {
675 | var o = [ { a: [ { b: 1 }, { b: 2 } ] }, { a: [ { b: 2 } ] } ]
676 | , t = trans(o).mapff(null, 'a.c', 'a', 'length').value();
677 | assert.deepEqual(t, [
678 | { a: [ { b: 1, c: 2 }, { b: 2, c: 2 } ] }
679 | , { a: [ { b: 2, c: 1 } ] } ]);
680 | });
681 |
682 | it('should allow setting a field on the source object 4', function () {
683 | var o = [ { a: [ { b: { c: 1 } }, { b: { c: 2 } } ] }, { a: [ { b: { c: 3 } } ] } ]
684 | , t = trans(o).mapff(null, 'a.b.d', 'a', 'length').value();
685 | assert.deepEqual(t, [
686 | { a: [ { b: { c: 1, d: 2 } }, { b: { c: 2, d: 2 } } ] }
687 | , { a: [ { b: { c: 3, d: 1 } } ] } ]);
688 | });
689 |
690 | it('should allow setting a field on the source object 5', function () {
691 | var o = [ { a: [ { b: { c: 1 } }, { b: { c: 2 } } ] }, { a: [ { b: { c: 3 } } ] } ]
692 | , t = trans(o).mapff('a.b', 'a.b.d', function (x) { return x.c + 1; }).value();
693 | assert.deepEqual(t, [
694 | { a: [ { b: { c: 1, d: 2 } }, { b: { c: 2, d: 3 } } ] }
695 | , { a: [ { b: { c: 3, d: 4 } } ] } ]);
696 | });
697 |
698 | it('should allow setting a field on the source object 6', function () {
699 | var o = { a: 1, b: 2 }
700 | , t = trans(o).mapff(null, 'c', function (x) { return x.a + x.b; }).value();
701 | assert.deepEqual(t, { a: 1, b: 2, c: 3 });
702 | });
703 |
704 | it('should allow setting a field on the source object 7', function () {
705 | var o = [ { b: 1, c: 3 }, { b: 2, c: 3 } ]
706 | , t = trans(o).mapff(null, 'd', function(x) { return x.b + x.c; }).value();
707 | assert.deepEqual(t, [ { b: 1, c: 3, d: 4 }, { b: 2, c: 3, d: 5 } ]);
708 | });
709 |
710 | it('should allow setting a field on the source object 8', function () {
711 | var o = { a: [ { b: 1, c: 3 }, { b: 2, c: 3 } ] }
712 | , t = trans(o).mapff('a', 'a.d', function(x) { return x.b + x.c; }).value();
713 | assert.deepEqual(t, { a: [ { b: 1, c: 3, d: 4 }, { b: 2, c: 3, d: 5 } ] });
714 | });
715 |
716 | it('should allow setting a field on the source object 9', function () {
717 | var o = { a: [ { b: 1, c: 3 }, { b: 2, c: 3 } ] }
718 | , t = trans(o).mapff('a', 'd', '.', function(x) { return x.b + x.c; }).value();
719 | assert.deepEqual(t, { a: [ { b: 1, c: 3 }, { b: 2, c: 3 } ], d: [ 4, 5 ] });
720 | });
721 |
722 | it('should allow setting a field on the source object 10', function () {
723 | var o = { a: [ { b: 1, c: 3 }, { b: 2, c: 3 } ] }
724 | , t = trans(o).mapff(null, 'a.d', function(obj) { return obj.a.length; }).value();
725 | assert.deepEqual(t, { a: [ { b: 1, c: 3, d: 2 }, { b: 2, c: 3, d: 2 } ] });
726 | });
727 |
728 | it('should allow setting a field on the source object 11', function () {
729 | var o = { a: [ { b: 1, c: 3 }, { b: 2, c: 3 } ] }
730 | , t = trans(o).mapff(null, 'a.d', 'a', '.', 'b').value();
731 | assert.deepEqual(t, { a: [ { b: 1, c: 3, d: [ 1, 2 ] }, { b: 2, c: 3, d: [ 1, 2 ] } ] });
732 | });
733 |
734 | it('should allow setting a field on the source object 12', function () {
735 | var o = [
736 | { a: [ { b: 1, c: 3 }, { b: 2, c: 3 } ] },
737 | { a: [ { b: 5, c: 3 }, { b: 7, c: 3 } ] } ]
738 | , t = trans(o).mapff('a', 'a.d', function(x) { return x.b + x.c; }).value();
739 | assert.deepEqual(t, [
740 | { a: [ { b: 1, c: 3, d: 4 }, { b: 2, c: 3, d: 5 } ] },
741 | { a: [ { b: 5, c: 3, d: 8 }, { b: 7, c: 3, d: 10 } ] }
742 | ]);
743 | });
744 |
745 | it('should allow setting a field on the source object - nested arrays', function () {
746 | var o = [
747 | [ { a: { b: 1, c: 10 } }, { a: { b: 2, c: 20 } } ]
748 | , [ { a: { b: 3, c: 30 } } ] ]
749 | , t = trans(o).mapff('a', 'a.e', function (x) { return x.b + x.c; }).value();
750 | assert.deepEqual(t, [
751 | [ { a: { b: 1, c: 10, e: 11 } }, { a: { b: 2, c: 20, e: 22 } } ]
752 | , [ { a: { b: 3, c: 30, e: 33 } } ]
753 | ]);
754 | });
755 |
756 | it('should fail when the destination field is not on an object', function () {
757 | assert.throws(function () {
758 | trans({ a: { b: 1 }, c: { d: 1 } }).mapff('a.b', 'c.d.e');
759 | }, Error);
760 | });
761 | });
762 | };
763 |
--------------------------------------------------------------------------------