Filter and display at the same iteration

I have such a simple situation when I want to filter and match a single value, for example:

 const files = results.filter(function(r){
      return r.file;
    })
    .map(function(r){
       return r.file;
    });

To save lines of code as well as increase performance, I am looking for:

const files = results.filterAndMap(function(r){
  return r.file;
});

Does this exist, or should I write something myself? I wanted this functionality in several places, I just never bothered to look into it before.

+4
source share
7 answers

If you really need to do this in 1 function, you need to use reduce, like this

results.reduce(
  // add the file name to accumulator if it exists
  (acc, result) => result.file ? acc.concat([result.file]) : acc,
  // pass empty array for initial accumulator value
  []
)

, concat push , .

, , for,

files = []
for (var i = 0; i < results.length; i++) {
  var file = results[i].file
  if (file) files.push(file)
}

, filter/map

+8

. , : mapReduce, filterReduce tapReduce; , .

const mapReduce = map => reduce =>
  (acc, x) => reduce (acc, map (x))
  
const filterReduce = filter => reduce =>
  (acc, x) => filter (x) ? reduce (acc, x) : acc
  
const tapReduce = tap => reduce =>
  (acc, x) => (tap (x), reduce (acc, x))

const tcomp = (f,g) =>
  k => f (g (k))

const concat = (xs,ys) =>
  xs.concat(ys)
  
const transduce = (...ts) => xs =>
  xs.reduce (ts.reduce (tcomp, k => k) (concat), [])

const main =
  transduce (
    tapReduce (x => console.log('with:', x)),
    filterReduce (x => x.file),
    tapReduce (x => console.log('has file:', x.file)),
    mapReduce (x => x.file),
    tapReduce (x => console.log('final:', x)))
      
const data =
  [{file: 1}, {file: undefined}, {}, {file: 2}]
  
console.log (main (data))
// with: { file: 1 }
// has file: 1
// final: 1
// with: { file: undefined }
// with: {}
// with: { file: 2 }
// has file: 2
// final: 2
// => [ 1, 2 ]
Hide result

API

, , API. .map, .filter, .whatever, , API - , , ,

// Trans Monoid
const Trans = f => ({
  runTrans: f,
  concat: ({runTrans: g}) =>
    Trans (k => f (g (k)))
})

Trans.empty = () =>
  Trans(k => k)

// transducer "primitives"
const mapper = f =>
  Trans (k => (acc, x) => k (acc, f (x)))
  
const filterer = f =>
  Trans (k => (acc, x) => f (x) ? k (acc, x) : acc)
  
const tapper = f =>
  Trans (k => (acc, x) => (f (x), k (acc, x)))
  
// chainable API
const Transduce = (t = Trans.empty()) => ({
  map: f =>
    Transduce (t.concat (mapper (f))),
  filter: f =>
    Transduce (t.concat (filterer (f))),
  tap: f =>
    Transduce (t.concat (tapper (f))),
  run: xs =>
    xs.reduce (t.runTrans ((xs,ys) => xs.concat(ys)), [])
})

// demo
const main = data =>
  Transduce()
    .tap (x => console.log('with:', x))
    .filter (x => x.file)
    .tap (x => console.log('has file:', x.file))
    .map (x => x.file)
    .tap (x => console.log('final:', x))
    .run (data)
    
const data =
  [{file: 1}, {file: undefined}, {}, {file: 2}]

console.log (main (data))
// with: { file: 1 }
// has file: 1
// final: 1
// with: { file: undefined }
// with: {}
// with: { file: 2 }
// has file: 2
// final: 2
// => [ 1, 2 ]
Hide result

API, 2

API , , Trans mapper, filterer .. - @ftor.

. , . , . Transduce 10 ; 28 - , , , , ,

// chainable API only (no external dependencies)
const Transduce = (t = k => k) => ({
  map: f =>
    Transduce (k => t ((acc, x) => k (acc, f (x)))),
  filter: f =>
    Transduce (k => t ((acc, x) => f (x) ? k (acc, x) : acc)),
  tap: f =>
    Transduce (k => t ((acc, x) => (f (x), k (acc, x)))),
  run: xs =>
    xs.reduce (t ((xs,ys) => xs.concat(ys)), [])
})

// demo (this stays the same)
const main = data =>
  Transduce()
    .tap (x => console.log('with:', x))
    .filter (x => x.file)
    .tap (x => console.log('has file:', x.file))
    .map (x => x.file)
    .tap (x => console.log('final:', x))
    .run (data)
    
const data =
  [{file: 1}, {file: undefined}, {}, {file: 2}]

console.log (main (data))
// with: { file: 1 }
// has file: 1
// final: 1
// with: { file: undefined }
// with: {}
// with: { file: 2 }
// has file: 2
// final: 2
// => [ 1, 2 ]
Hide result

>

, for, . , .map/.filter/.whatever, .

mapReduce, . , mapReduce , .

, , , monoid - . ( ), , ... !

+8

, , . https://jsperf.com/filter-than-map-or-reduce/1

.

enter image description here

NodeJS ( npm i benchmark)

var suite = new (require('benchmark')).Suite

function getSampleInput() {
  return [{file: 'foo'}, {other: 'bar'}, {file: 'baz'}, {file: 'quux'}, {other: 'quuxdoo'}, {file: 'foobar'}, {file: 'foo'}, {other: 'bar'}, {file: 'baz'}, {file: 'quux'}, {other: 'quuxdoo'}, {file: 'foobar'}, {file: 'foo'}, {other: 'bar'}, {file: 'baz'}, {file: 'quux'}, {other: 'quuxdoo'}, {file: 'foobar'}, {file: 'foo'}, {other: 'bar'}, {file: 'baz'}, {file: 'quux'}, {other: 'quuxdoo'}, {file: 'foobar'}, {file: 'foo'}, {other: 'bar'}, {file: 'baz'}, {file: 'quux'}, {other: 'quuxdoo'}, {file: 'foobar'}, {file: 'foo'}, {other: 'bar'}, {file: 'baz'}, {file: 'quux'}, {other: 'quuxdoo'}, {file: 'foobar'}, {file: 'foo'}, {other: 'bar'}, {file: 'baz'}, {file: 'quux'}, {other: 'quuxdoo'}, {file: 'foobar'}, {file: 'foo'}, {other: 'bar'}, {file: 'baz'}, {file: 'quux'}, {other: 'quuxdoo'}, {file: 'foobar'}]
}

// author https://stackoverflow.com/users/3716153/gaafar 
function reduce(results) {
  return results.reduce(
    (acc, result) => result.file ? acc.concat([result.file]) : acc ,
    []
  )  
}

// author https://stackoverflow.com/users/1223975/alexander-mills
function filterThanMap(results) {
  return results.filter(function(r){
    return r.file;
  })
  .map(function(r){
     return r.file;
  });
}

// author https://stackoverflow.com/users/5361130/ponury-kostek
function forEach(results) {
  const files = [];

  results.forEach(function(r){
    if(r.file) files.push(r.file); 
  });

  return files
}

suite
  .add('filterThanMap', function() {filterThanMap(getSampleInput())})
  .add('reduce', function() {reduce(getSampleInput())})
  .add('forEach', function() {forEach(getSampleInput())})
  .on('complete', function() {
    console.log('results:')
    this.forEach(function(result) {
      console.log(result.name, result.count, result.times.elapsed)
    })
    console.log('the fastest is', this.filter('fastest').map('name')[0])
  })
  .run()
+3

forEach?

const files = [];
results.forEach(function(r){
  if(r.file) {
    files.push(r.file);  
  }
});
Hide result

If it’s not so fast, you can use fast.jsand do some other micro-optimizations.

const files = [];
const length = results.length;
for(var i = 0; i < length; i++) {
  if (results[i].file) {
    files[files.length] = results[i].file;
  }
}
Run codeHide result
+2
source

You can use a value o.fileor concat with an empty array for the result.

results.reduce((r, o) => r.concat(o.file || []), []);
+1
source

you can use Array.prototype.reduce()

const results = [{file:{file:1}}, {notfile:{file:1}}];

const files = results.reduce(function(arr, r){
                return r.file ? arr = [...arr, r.file.file] : arr;
              }, []);
              
console.log(files); // 1
Run codeHide result
0
source
   const file = (array) => {
     return array.reduce((acc,curr) => curr.file ? acc.concat(curr) : acc, 
     [])
    } 

Process:

acc to initiate as [] (empty array). reduce documents

-1
source

Source: https://habr.com/ru/post/1681663/


All Articles