1- const { Either, either, left, fromNullable } = require ( './src/either' )
2- const { List } = require ( './src/list' )
3-
4- const id = _ => _
5- const always = a => ( ) => a
1+ function id ( _ ) {
2+ return _
3+ }
64
75const DEFAULTS = {
86 types : { } ,
@@ -12,46 +10,35 @@ const DEFAULTS = {
1210 postFilters : [ ]
1311}
1412
15- /* --- Functional Utilities --- */
16- const map = fn => x => x . map ( fn )
17- const join = m => m . join ( )
18- // const chain = fn => m => m.chain(fn)
1913const compose = ( ...fns ) => ( res , ...args ) =>
2014 fns . reduceRight ( ( accum , next ) => next ( accum , ...args ) , res )
2115
22- const reduce = fn => zero => xs => xs . reduce ( fn , zero )
23- /* ---------------------------- */
24-
25- const split = d => s => s . split ( d )
26-
27- const createRootObj = compose (
28- either ( Object . create , Array ) ,
29- n => ( isNaN ( n ) ? left ( null ) : Either . of ( n ) ) ,
30- Number
31- )
32-
33- const handleArrays = _ => ( Array . isArray ( _ ) ? List . of ( _ ) : Either . of ( _ ) )
34-
35- const normalizeField = delimiter => m =>
36- fromNullable ( m )
37- . map ( m => m . indexOf ( delimiter ) > - 1 )
38- . map ( b => ( b ? m : m + delimiter + m ) )
39- . map ( split ( delimiter ) )
40-
41- const getMapSpec = delimiter => mapping =>
42- fromNullable ( mapping )
43- . map ( Array . isArray )
44- . map ( b => ( b ? Either . of ( mapping ) : left ( mapping ) ) )
45- . map (
46- either (
47- compose ( join , normalizeField ( delimiter ) ) ,
48- reduce ( ( spec , field ) =>
49- normalizeField ( delimiter ) ( field )
50- . map ( ( [ source , target ] ) => [ [ ...spec [ 0 ] , source ] , target ] )
51- . join ( )
52- ) ( [ [ ] , null ] )
53- )
16+ const createRootObj = n =>
17+ isNaN ( n ) ? Object . create ( null ) : new Array ( Number ( n ) )
18+
19+ const normalizeField = delimiter => m => {
20+ if ( m . indexOf ( delimiter ) > - 1 ) {
21+ return m . split ( delimiter )
22+ }
23+
24+ return [ m , m ]
25+ }
26+
27+ const getMapSpec = delimiter => {
28+ const normalizer = normalizeField ( delimiter )
29+
30+ return mapping => {
31+ if ( ! Array . isArray ( mapping ) ) return normalizer ( mapping )
32+
33+ return mapping . reduce (
34+ ( spec , field ) => {
35+ const [ source , target ] = normalizer ( field )
36+ return [ [ ...spec [ 0 ] , source ] , target ]
37+ } ,
38+ [ [ ] , null ]
5439 )
40+ }
41+ }
5542
5643const normalizeMapping = mapping =>
5744 typeof mapping === 'string' ? { field : mapping } : mapping
@@ -63,67 +50,59 @@ const getMappingFilter = (type, types) => {
6350 return id
6451}
6552
66- const getKey = reduce ( ( accum , k ) => ( accum ? accum [ k ] : undefined ) )
67-
68- const get = ( key , delimiter = DEFAULTS . objDelimiter ) => obj =>
69- compose (
70- either ( always ( obj ) , getKey ( obj ) ) ,
71- map ( split ( delimiter ) ) ,
72- fromNullable
73- ) ( key )
74-
75- const setKey = value =>
76- reduce ( ( accum , key , i , array ) => {
77- if ( i === array . length - 1 ) accum [ key ] = value
78- else if ( ! accum [ key ] ) accum [ key ] = createRootObj ( array [ i + 1 ] )
79- return accum [ key ]
80- } )
81-
82- const assign = ( key , delimiter = DEFAULTS . objDelimiter ) => ( obj , value ) =>
83- compose (
84- always ( obj ) ,
85- either ( id , setKey ( value ) ( obj ) ) ,
86- map ( split ( delimiter ) ) ,
87- fromNullable
88- ) ( key )
53+ const get = ( key , delimiter = DEFAULTS . objDelimiter ) => {
54+ if ( key == null ) return id
55+
56+ const spec = key . split ( delimiter )
57+
58+ return obj => spec . reduce ( ( a , k ) => ( a ? a [ k ] : undefined ) , obj )
59+ }
60+
61+ const assign = ( key , delimiter = DEFAULTS . objDelimiter ) => {
62+ if ( key == null ) return id
63+
64+ const spec = key . split ( delimiter )
65+ return ( obj , value ) => {
66+ spec . reduce ( ( accum , key , i , array ) => {
67+ if ( i === array . length - 1 ) accum [ key ] = value
68+ else if ( ! accum [ key ] ) accum [ key ] = createRootObj ( array [ i + 1 ] )
69+ return accum [ key ]
70+ } , obj )
71+ return obj
72+ }
73+ }
8974
9075class Mapper {
9176 constructor ( options ) {
9277 this . config = Object . assign ( { } , DEFAULTS , options )
78+ this . getMapSpec = getMapSpec ( this . config . mapDelimiter )
79+
80+ this . mapFn = compose (
81+ ...this . config . postFilters ,
82+ ( v , m , con , c , a ) =>
83+ getMappingFilter ( m . type , this . config . types ) ( v , m , con , c , a ) ,
84+ ...this . config . preFilters
85+ )
9386 }
9487
9588 map ( mappings , curr , next = Object . create ( null ) ) {
96- return mappings . map ( normalizeMapping ) . reduce (
97- ( accum , mapping ) =>
98- fromNullable ( mapping . field )
99- . chain ( getMapSpec ( this . config . mapDelimiter ) )
100- . chain ( ( [ sourceField , targetField ] ) =>
101- Either . of ( sourceField )
102- . map ( handleArrays )
103- . map ( map ( field => get ( field , this . config . objDelimiter ) ( curr ) ) )
104- . map ( join )
105- . map ( _ =>
106- compose (
107- /* End user-land transforms */
108- ...this . config . postFilters ,
109- getMappingFilter ( mapping . type , this . config . types ) ,
110- ...this . config . preFilters
111- /* Begin user-land transforms */
112- ) ( _ , mapping , this . config , curr , accum )
113- )
114- . map ( fromNullable )
115- . chain (
116- either (
117- always ( accum ) ,
118- assign ( targetField , this . config . objDelimiter ) . bind (
119- this ,
120- accum
121- )
122- )
123- )
124- ) ,
125- next
126- )
89+ return mappings . map ( normalizeMapping ) . reduce ( ( accum , mapping ) => {
90+ const [ sourceField , targetField ] = this . getMapSpec ( mapping . field )
91+
92+ const getter = field => get ( field , this . config . objDelimiter ) ( curr )
93+
94+ let value = Array . isArray ( sourceField )
95+ ? sourceField . map ( getter )
96+ : getter ( sourceField )
97+
98+ value = this . mapFn ( value , mapping , this . config , curr , accum )
99+
100+ if ( value === undefined ) {
101+ return accum
102+ }
103+
104+ return assign ( targetField , this . config . objDelimiter ) ( accum , value )
105+ } , next )
127106 }
128107}
129108
0 commit comments