map-factory
Advanced tools
Comparing version 1.2.0 to 1.2.1
{ | ||
"name": "map-factory", | ||
"version": "1.2.0", | ||
"version": "1.2.1", | ||
"description": "Object mapping tool", | ||
@@ -18,4 +18,3 @@ "main": "./dist/lib/index.js", | ||
"t": "nodeunit ./dist/test/", | ||
"pretravis": "npm run build", | ||
"travis": "npm run coveralls", | ||
"travis": "npm run test", | ||
"premajor": "npm run test", | ||
@@ -57,3 +56,3 @@ "major": "npm version major -m \"published to npm as v%s\" && git push --follow-tags && npm publish", | ||
"rimraf": "^2.5.3", | ||
"tslint": "^3.13.0", | ||
"tslint": "3.13.0", | ||
"typescript": "^1.8.10" | ||
@@ -60,0 +59,0 @@ }, |
341
README.md
# map-factory | ||
[](https://coveralls.io/github/midknight41/map-factory?branch=master) [](https://travis-ci.org/midknight41/map-factory) [](https://david-dm.org/midknight41/map-factory#info=dependencies) [](https://david-dm.org/midknight41/map-factory#info=devDependencies) | ||
@@ -7,22 +6,37 @@ | ||
A simple utility that greatly simplifies mapping data from one shape to another. **map-factory** provides a fluent interface and supports deep references, custom transformations, and object merging. | ||
A simple object mapping utility that makes it easy to map data from one object to another. **map-factory** provides a fluent interface and supports deep references, custom transformations, and object merging. | ||
This is an alternative interface for the excellent [object-mapper](http://www.npmjs.com/object-mapper). | ||
#### Features | ||
- Deep references with dot notation | ||
- Custom transformations | ||
- Fluent (chainable) interface | ||
- Select from multiple source fields in a single statement | ||
#### Examples | ||
- [Mapping data to a new structure](#mapping) | ||
- [Working with arrays](#arrays) | ||
- [Transformations](#transforms) | ||
- [Working with multiple source objects](#multisource) | ||
See [Change Log](./CHANGELOG.md) for changes from previous versions. | ||
## How to install | ||
## Usage | ||
``` | ||
npm install map-factory | ||
``` | ||
**map-factory** supports two similar interfaces. Which you use is up to you and your use case. | ||
## Getting Started | ||
The first is a fluent interface: | ||
**map-factory** supports two similar interfaces. Which you use is up to you and your use case. There is no functional difference. | ||
The basic syntax is: | ||
```js | ||
const createMapper = require("map-factory"); | ||
const mapper = createMapper(); | ||
const map = createMapper(); | ||
mapper | ||
.map("sourceField").to("source.field") | ||
.map("sourceId").to("source.id"); | ||
map("sourceField").to("source.field"); | ||
map("sourceId").to("source.id"); | ||
@@ -32,3 +46,3 @@ const result = map.execute(source); | ||
Alternatively you can you the slightly shorter version: | ||
Alternatively you can use the fluent interface which supports method chaining. This syntax is better when you need to refer to multiple mappers in your code. | ||
@@ -38,6 +52,7 @@ ```js | ||
const map = createMapper(); | ||
const mapper = createMapper(); | ||
map("sourceField").to("source.field"); | ||
map("sourceId").to("source.id"); | ||
mapper | ||
.map("sourceField").to("source.field") | ||
.map("sourceId").to("source.id"); | ||
@@ -47,10 +62,11 @@ const result = map.execute(source); | ||
There is no functional difference between the two and they can be used interchangeably. | ||
## Examples | ||
<a name="mapping"/> | ||
### Mapping data to a new structure | ||
## Map a source field to the same object structure | ||
**map-factory** supports deep object references for both source and target fields via dot notation. Mapping is explicit so unmapped fields are discarded. | ||
Mapping is explicit so unmapped fields are discarded. | ||
```js | ||
const createMapper = require("map-factory"); | ||
const assert = require("assert"); | ||
@@ -64,23 +80,20 @@ const source = { | ||
const map = createMapper(); | ||
map("fieldName").to("field.name"); | ||
map("fieldId").to("field.id"); | ||
map("fieldName"); | ||
map("fieldId"); | ||
const result = map.execute(source); | ||
console.log(result); | ||
/* | ||
{ | ||
"fieldName": "name1", | ||
"fieldId": "123" | ||
assert.deepEqual(result, { | ||
"field": { | ||
"name": "name1", | ||
"id": "123" | ||
} | ||
*/ | ||
}); | ||
``` | ||
## Map a source field to a different object structure | ||
The ```to()``` is optional so if you want to just want to copy a subset of fields to a new object you can do the following: | ||
Of course, we probably want a different structure for our target object. | ||
```js | ||
const createMapper = require("map-factory"); | ||
const assert = require("assert"); | ||
@@ -94,22 +107,20 @@ const source = { | ||
const map = createMapper(); | ||
map("fieldName"); | ||
map("fieldId"); | ||
map("fieldName").to("field.name"); | ||
map("fieldId").to("field.id"); | ||
const result = map.execute(source); | ||
console.log(result); | ||
/* | ||
{ | ||
"field": { | ||
"name": "name1", | ||
"id": "123" | ||
} | ||
} | ||
*/ | ||
assert.deepEqual(result, { | ||
"fieldName": "name1", | ||
"fieldId": "123" | ||
}); | ||
``` | ||
<a name="arrays"/> | ||
### Working with arrays | ||
You can use ```[]``` to traverse the entries in an array. For example, here you can transform an array of objects to an array of strings. | ||
## Supports deep references for source and target objects | ||
```js | ||
const createMapper = require("map-factory"); | ||
const assert = require("assert"); | ||
```js | ||
const source = { | ||
@@ -135,3 +146,2 @@ "person": { | ||
const map = createMapper(); | ||
map("person.email").to("user.login"); | ||
@@ -142,13 +152,10 @@ map("account.id").to("user.accountId"); | ||
const result = map.execute(source); | ||
console.log(result); | ||
/* | ||
{ | ||
"user": { | ||
"login": "john@someplace.com", | ||
"accountId": "abc123", | ||
"entitlements": ["game-1", "game-2"] | ||
} | ||
assert.deepEqual(result, { | ||
"user": { | ||
"login": "john@someplace.com", | ||
"accountId": "abc123", | ||
"entitlements": ["game-1", "game-2"] | ||
} | ||
*/ | ||
}); | ||
``` | ||
@@ -160,2 +167,3 @@ | ||
const createMapper = require("map-factory"); | ||
const assert = require("assert"); | ||
@@ -180,10 +188,7 @@ const source = { | ||
const map = createMapper(); | ||
map("articles.[0]").to("topStory"); | ||
const result = map.execute(source); | ||
console.log(result); | ||
/* | ||
{ | ||
assert.deepEqual(result, { | ||
"topStory": { | ||
@@ -195,10 +200,11 @@ "id": 1, | ||
} | ||
} | ||
*/ | ||
}); | ||
``` | ||
<a name="transforms"/> | ||
### Transformations | ||
More complicated transformations can be handled by providing a function. The selected source data will be passed to the function. | ||
More complicated transformations can be handled by providing a function. | ||
```js | ||
const createMapper = require("map-factory"); | ||
const assert = require("assert"); | ||
@@ -223,7 +229,6 @@ const source = { | ||
const map = createMapper(); | ||
map("articles.[0]").to("topStory"); | ||
map("articles").to("otherStories", articles => { | ||
// We don't want to include the top story | ||
// We don't want to include the top story in with the other stories | ||
articles.shift(); | ||
@@ -236,61 +241,20 @@ | ||
const result = map.execute(source); | ||
console.log(result); | ||
/* | ||
{ | ||
"topStory": { | ||
"id": 1, | ||
"title": "Top Article", | ||
assert.deepEqual(result, { | ||
"topStory": { | ||
"id": 1, | ||
"title": "Top Article", | ||
"author": "Joe Doe", | ||
"body": "..." | ||
}, | ||
"otherStories": [ | ||
{ | ||
"id": 2, | ||
"title": "Second Article", | ||
"author": "Joe Doe", | ||
"body": "..." | ||
}, | ||
"otherStories": [ | ||
{ | ||
"id": 2, | ||
"title": "Second Article", | ||
"author": "Joe Doe", | ||
"body": "..." | ||
} | ||
] | ||
} | ||
*/ | ||
``` | ||
An existing object can be provided as the target object. | ||
```js | ||
const createMapper = require("map-factory"); | ||
const source = { | ||
"fieldName": "name1", | ||
"fieldId": "123", | ||
"fieldDescription": "description" | ||
}; | ||
const destination = { | ||
"existing": "data" | ||
}; | ||
const map = createMapper(); | ||
map("fieldName").to("field.name"); | ||
map("fieldId").to("field.id"); | ||
const result = map.execute(source, destination); | ||
console.log(result); | ||
/* | ||
{ | ||
"existing": "data", | ||
"field": { | ||
"name": "name1", | ||
"id": "123" | ||
} | ||
} | ||
*/ | ||
] | ||
}); | ||
``` | ||
## Select from multiple sources at once | ||
You can also provide an array of source fields and they can be extracted together if you provide a transform for the target field. | ||
@@ -300,2 +264,3 @@ | ||
const createMapper = require("map-factory"); | ||
const assert = require("assert"); | ||
@@ -320,16 +285,12 @@ const source = { | ||
const result = map.execute(source); | ||
console.log(result); | ||
/* | ||
{ | ||
fruit: { | ||
count: 7 | ||
} | ||
assert.deepEqual(result, { | ||
fruit: { | ||
count: 7 | ||
} | ||
*/ | ||
}); | ||
``` | ||
## Common patterns | ||
<a name="multisource"/> | ||
### Dealing with multiple sources of data | ||
@@ -350,2 +311,3 @@ | ||
const createMapper = require("map-factory"); | ||
const assert = require("assert"); | ||
@@ -379,25 +341,52 @@ // assume the following inputs | ||
const final = map.execute(source); | ||
console.log(final); | ||
const result = map.execute(source); | ||
/* | ||
{ | ||
"blog": | ||
assert.deepEqual(result, { | ||
"blog": { | ||
"post": | ||
{ | ||
"post": | ||
{ | ||
"text": "<p>Some Text</p>", | ||
"comments": ["not too bad", "pretty good", "awful"], | ||
"topComment": "not too bad" | ||
}, | ||
"author": { | ||
"id": 123, | ||
"name": "John Doe", | ||
"email": "john.doe@nobody.com" | ||
} | ||
"text": "<p>Some Text</p>", | ||
"comments": ["not too bad", "pretty good", "awful"], | ||
"topComment": "not too bad" | ||
}, | ||
"author": { | ||
"id": 123, | ||
"name": "John Doe", | ||
"email": "john.doe@nobody.com" | ||
} | ||
} | ||
*/ | ||
}); | ||
``` | ||
An existing object can be provided as the target object. | ||
```js | ||
const createMapper = require("map-factory"); | ||
const assert = require("assert"); | ||
const source = { | ||
"fieldName": "name1", | ||
"fieldId": "123", | ||
"fieldDescription": "description" | ||
}; | ||
const destination = { | ||
"existing": "data" | ||
}; | ||
const map = createMapper(); | ||
map("fieldName").to("field.name"); | ||
map("fieldId").to("field.id"); | ||
const result = map.execute(source, destination); | ||
assert.deepEqual(result, { | ||
"existing": "data", | ||
"field": { | ||
"name": "name1", | ||
"id": "123" | ||
} | ||
}); | ||
``` | ||
#### Merge objects with multiple mappers | ||
@@ -408,2 +397,3 @@ The other option is to decorate your existing data objects in a piece by piece fashion using the merge ability. Note that when using a named mapper like ```postMapper``` the code reads better when you use the explicit ```map()``` method. | ||
const createMapper = require("map-factory"); | ||
const assert = require("assert"); | ||
@@ -444,2 +434,21 @@ // assume the following inputs | ||
result = authorMapper.execute(user, result); | ||
assert.deepEqual(result, { | ||
"blog": { | ||
"post": { | ||
"text": "<p>Some Text</p>", | ||
"comments": [ | ||
"not too bad", | ||
"pretty good", | ||
"awful" | ||
], | ||
"topComment": "not too bad" | ||
}, | ||
"author": { | ||
"id": 123, | ||
"name": "John Doe", | ||
"email": "john.doe@nobody.com" | ||
} | ||
} | ||
}); | ||
``` | ||
@@ -451,2 +460,5 @@ | ||
```js | ||
const createMapper = require("map-factory"); | ||
const assert = require("assert"); | ||
const BlogRepo = require("./artifacts/mock-blog-repo"); | ||
@@ -459,7 +471,6 @@ class BlogService { | ||
// initialise mapper | ||
this.authorMapper = createMapper(); | ||
this.authorMapper.map("id").to("blog.author.id"); | ||
this.authorMapper.map("name").to("blog.author.name"); | ||
this.authorMapper.map("email").to("blog.author.email"); | ||
this.authorMapper = createMapper() | ||
.map("id").to("blog.author.id") | ||
.map("name").to("blog.author.name") | ||
.map("email").to("blog.author.email"); | ||
} | ||
@@ -474,2 +485,34 @@ | ||
} | ||
const blogService = new BlogService(new BlogRepo()); | ||
const post = { | ||
"blog": { | ||
"post": { | ||
"id": 10, | ||
"title": "Foo bar baz", | ||
"post": "<p>Foo bar baz</p><Foo bar baz</p>" | ||
} | ||
} | ||
} | ||
return blogService.decorateBlogPostWithAuthor(1, post) | ||
.then(result => { | ||
assert.deepEqual(result, { | ||
"blog": { | ||
"post": { | ||
"id": 10, | ||
"title": "Foo bar baz", | ||
"post": "<p>Foo bar baz</p><Foo bar baz</p>" | ||
}, | ||
"author": { | ||
"id": 1, | ||
"name": "foo", | ||
"email": "foo@foobar.com" | ||
} | ||
} | ||
}); | ||
}); | ||
``` | ||
This module is an alternative interface for the excellent [object-mapper](http://www.npmjs.com/object-mapper). |
67383
497