@@ -128,29 +128,29 @@ function iterate (obj, fn=constants.identity, col=1, type, iter) {
128128}
129129
130130// Async alias
131- async function iterateFA ( obj , fn = constants . identity , col , retFn , res , valFltr , retFltr , type , iter , iterA , fnA ) {
131+ async function iterateFA ( obj , fn = constants . identity , col , retFn , res , valFltr , retFltr , accFn , type , iter , iterA , fnA ) {
132132 await iterateA ( obj , async ( val , key , obj ) => {
133133 let ret ;
134134 if ( valFltr ) {
135135 if ( valFltr ( val ) ) {
136- ret = await fn ( val , key , obj ) ;
136+ ret = accFn ? await fn ( accFn ( val ) , val , key , obj ) : await fn ( val , key , obj ) ;
137137 } else {
138138 return ;
139139 }
140140 } else {
141- ret = await fn ( val , key , obj ) ;
141+ ret = accFn ? await fn ( accFn ( val ) , val , key , obj ) : await fn ( val , key , obj ) ;
142142 }
143143 if ( ret === constants . BREAK ) {
144144 return ret ;
145145 }
146146 if ( retFltr ) {
147147 if ( retFltr ( ret ) ) {
148- return retFn ? retFn ( res , ret , val , key ) : ret ;
148+ return retFn ? retFn ( res , ret , val , key , accFn ) : ret ;
149149 } else {
150150 return ;
151151 }
152152 } else {
153- return retFn ? retFn ( res , ret , val , key ) : ret ;
153+ return retFn ? retFn ( res , ret , val , key , accFn ) : ret ;
154154 }
155155 } , col , type , iter , iterA ) ;
156156 return types . isFunction ( res ) ? res ( ) : res ;
@@ -159,31 +159,31 @@ async function iterateFA (obj, fn=constants.identity, col, retFn, res, valFltr,
159159// Run an iterator fn for each item in obj with filters
160160// Accepts optional return function, return value, value filter, and return value filter
161161// Note: Useful for composing other types of iteration methods
162- // Note: Return function, value filter, and return filter are not validated
162+ // Note: Return function, value filter, return filter, and accumulator function are not validated
163163// Note: Can break iteration early by returning BREAK symbol
164- function iterateF ( obj , fn = constants . identity , col , retFn , res , valFltr , retFltr , type , iter ) {
164+ function iterateF ( obj , fn = constants . identity , col , retFn , res , valFltr , retFltr , accFn , type , iter ) {
165165 iterate ( obj , ( val , key , obj ) => {
166166 let ret ;
167167 if ( valFltr ) {
168168 if ( valFltr ( val ) ) {
169- ret = fn ( val , key , obj ) ;
169+ ret = accFn ? fn ( accFn ( val ) , val , key , obj ) : fn ( val , key , obj ) ;
170170 } else {
171171 return ;
172172 }
173173 } else {
174- ret = fn ( val , key , obj ) ;
174+ ret = accFn ? fn ( accFn ( val ) , val , key , obj ) : fn ( val , key , obj ) ;
175175 }
176176 if ( ret === constants . BREAK ) {
177177 return ret ;
178178 }
179179 if ( retFltr ) {
180180 if ( retFltr ( ret ) ) {
181- return retFn ? retFn ( res , ret , val , key ) : ret ;
181+ return retFn ? retFn ( res , ret , val , key , accFn ) : ret ;
182182 } else {
183183 return ;
184184 }
185185 } else {
186- return retFn ? retFn ( res , ret , val , key ) : ret ;
186+ return retFn ? retFn ( res , ret , val , key , accFn ) : ret ;
187187 }
188188 } , col , type , iter ) ;
189189 return types . isFunction ( res ) ? res ( ) : res ;
@@ -209,7 +209,7 @@ function isAsync (iter, iterA, fnA) {
209209
210210// Compose iteration method
211211// Note: Checks type info
212- // Returns sync or async based on obj and fn types
212+ // Returns sync or async based on obj and fn type signatures
213213function compose ( obj , fn , col = 1 ) {
214214 let type = types . getType ( obj ) ;
215215 let iter = types . isIterable ( obj ) ;
@@ -225,18 +225,18 @@ function compose (obj, fn, col=1) {
225225
226226// Compose iteration method with filters
227227// Note: Checks type info
228- // Returns sync or async based on obj and fn types
229- function composeF ( obj , fn , col = 1 , retFn , res , valFltr , retFltr ) {
228+ // Returns sync or async based on obj and fn type signatures
229+ function composeF ( obj , fn , col = 1 , retFn , res , valFltr , retFltr , accFn ) {
230230 let type = types . getType ( obj ) ;
231231 let iter = types . isIterable ( obj ) ;
232232 let iterA = types . isAsyncIterable ( obj ) ;
233233 let fnA = types . isAsyncFunction ( fn ) ;
234234 let async = isAsync ( iter , iterA , fnA ) ;
235235 fn = types . toFn ( fn , constants . identity ) ;
236236 if ( async ) {
237- return iterateFA ( obj , fn , col , retFn , res , valFltr , retFltr , type , iter , iterA ) ;
237+ return iterateFA ( obj , fn , col , retFn , res , valFltr , retFltr , accFn , type , iter , iterA ) ;
238238 }
239- return iterateF ( obj , fn , col , retFn , res , valFltr , retFltr , type , iter ) ;
239+ return iterateF ( obj , fn , col , retFn , res , valFltr , retFltr , accFn , type , iter ) ;
240240}
241241
242242// Run an iterator fn for each item in obj
@@ -414,6 +414,64 @@ function findNotNil (obj, fn, col) {
414414 return composeF ( obj , fn , col , findFn , resFn ( ) , types . notNil , types . notNil ) ;
415415}
416416
417+ const GET = Symbol ( ) ;
418+ const SET = Symbol ( ) ;
419+
420+ function reduceFn ( res , ret , val , key , accFn ) {
421+ return res ( accFn ( ret , SET ) ) ;
422+ }
423+
424+ // Getter/Setter
425+ // Each iteration receives acc as the return value from the previous call
426+ function reduceAcc ( acc ) {
427+ let init = types . isNil ( acc ) ;
428+ return ( val , action = GET ) => {
429+ if ( init ) {
430+ acc = types . of ( val ) ;
431+ init = 0 ;
432+ }
433+ if ( action === SET ) {
434+ return acc = val ;
435+ }
436+ return acc ;
437+ }
438+ }
439+
440+ // Reduce obj to accumulated result from running each item through iterator fn
441+ function reduce ( obj , fn , acc , col ) {
442+ return composeF ( obj , fn , col , reduceFn , resFn ( ) , undefined , undefined , reduceAcc ( acc ) ) ;
443+ }
444+
445+ // Alias for reduce (ignores null and undefined)
446+ function reduceNotNil ( obj , fn , acc , col ) {
447+ return composeF ( obj , fn , col , reduceFn , resFn ( ) , types . notNil , types . notNil , reduceAcc ( acc ) ) ;
448+ }
449+
450+ function transformFn ( res , ret , val , key , accFn ) {
451+ return res ( accFn ( ) ) ;
452+ }
453+
454+ // Getter/Setter
455+ // Each iteration receives acc as the same initial value
456+ function transformAcc ( acc , obj ) {
457+ if ( types . isNil ( acc ) ) {
458+ acc = types . of ( obj ) ;
459+ }
460+ return ( val , action = GET ) => {
461+ return acc ;
462+ }
463+ }
464+
465+ // Transform obj to new accumulated result from running each item through iterator fn
466+ function transform ( obj , fn , acc , col ) {
467+ return composeF ( obj , fn , col , transformFn , resFn ( ) , undefined , undefined , transformAcc ( acc , obj ) ) ;
468+ }
469+
470+ // Alias for transform (ignores null and undefined)
471+ function transformNotNil ( obj , fn , acc , col ) {
472+ return composeF ( obj , fn , col , transformFn , resFn ( ) , types . notNil , types . notNil , transformAcc ( acc , obj ) ) ;
473+ }
474+
417475exports . drop = drop ;
418476exports . dropNotNil = dropNotNil ;
419477exports . each = each ;
@@ -431,6 +489,8 @@ exports.iterate = compose;
431489exports . iterateF = composeF ;
432490exports . map = map ;
433491exports . mapNotNil = mapNotNil ;
492+ exports . reduce = reduce ;
493+ exports . reduceNotNil = reduceNotNil ;
434494exports . remove = remove ;
435495exports . removeNotNil = removeNotNil ;
436496exports . some = some ;
@@ -439,3 +499,5 @@ exports.take = take;
439499exports . takeNotNil = takeNotNil ;
440500exports . tap = tap ;
441501exports . tapNotNil = tapNotNil ;
502+ exports . transform = transform ;
503+ exports . transformNotNil = transformNotNil ;
0 commit comments