DEV Community

artydev
artydev

Posted on • Updated on

Enhancing our toolbox

There is a little problems with some of our functions.


const pickprop = (prop) => (obj) => obj[prop] ?? null; 

const map = (f) => (arr) => arr.map(f);

const filter = (f) => (arr) => arr.filter(f);

Enter fullscreen mode Exit fullscreen mode

Given :

let pets = [
  {
    petname : "Bill",
    breed : "poodle",
    weight: 12,
    owner : {
      ownername : "Paul",
      contribution : {
        amount : 32,
        payed : false
      },
      address : {
        city :  "Paris"
      }
    }
  },
  {
    petname : "Maya",
    race : "pointer",
    weight: 27,
    owner : {
      ownername : "Henri",
      contribution :  {
        amount : 12,
        payed : true
      },
      address : {
         city :  "London"
      }
    }
  },
  {
    petname : "Ooper",
    race : "setter",
    weight: 20,
    owner : {
      ownername : "Nicolas",
      contribution :  {
        amount : 12,
        payed : true
      },
      address : {
         city :  "London"
      }
    }
  }
]
Enter fullscreen mode Exit fullscreen mode

We can only pass one argument at a time. So we can not write :


const petNameOne = pickprop("petname", pets[0])

Enter fullscreen mode Exit fullscreen mode

That's because pickprop is not really curried

So, let's "spice" them :



const curry = function (fn) {
  const curried = (...args) => {
    if (args.length >= fn.length) {
      return fn.apply({}, args)
    }
    else {
      return (...vargs) => curried.apply({}, args.concat(vargs))
    }
  }
  return curried
}

const compose = 
    (...fns) => arg => fns.reduceRight((acc, f) => f(acc), arg);

const pipe = 
    (...fns) => arg => fns.reduce((acc, f) => f(acc), arg);

const asyncpipe = 
    (...fns) => arg => fns.reduce((p, fn) => p.then(fn), Promise.resolve(arg));

const pickprop = curry((prop, obj) => obj[prop] ?? null); 

const map = curry((f, arr) => arr.map(f));

const filter = curry((f, arr) => arr.filter(f));

Enter fullscreen mode Exit fullscreen mode

We can now call our function pickprop using two syntax :


let petNameOne = pickprop("petname", pets[0])

console.log(petNameOne)

petNameOne = pickprop("petname")(pets[0])

console.log(petNameOne)


Enter fullscreen mode Exit fullscreen mode

Great :-)

You can play with the demo here : Demo

Top comments (15)

Collapse
 
artydev profile image
artydev • Edited

Thank you for your question Eckehard, at first sight here is what I would do:


const test = (x, i) =>  console.log(x + i) 

function _loop (n, fn, args) {
  [...Array(n)].forEach((_, i) => fn([...args].join(''), i + 1))
}

_loop(6, test, "This is a test ");

console.log("_____________________________")

const loop = curry(_loop);

loop(5, test, "this is a test ");

loop(5, test)("this is a test ");

loop(5)(test)("this is a test ");

loop(5)(test, "this is a test ");


Enter fullscreen mode Exit fullscreen mode

Demo

Collapse
 
artydev profile image
artydev • Edited

FInally you could write :


const test = (x, i) =>  console.log(x + i) 

const loop = curry((n, fn, args) =>
  [...Array(n)].forEach((_, i) => fn(args, i + 1)));

loop(5)(test, "this is a test ");

Enter fullscreen mode Exit fullscreen mode

Demo

Collapse
 
artydev profile image
artydev

Thanks, to gave me the idea to include such a loop, in my micro lib :-)

Collapse
 
efpage profile image
Eckehard • Edited

Do you know a way to build a simplified but readable loop? Instead of for( let i=0; i<5; i++) fn(args)

Ideally it would be

loop(5) { ... }

We can do things like this, but this is far from ideal:

function loop (n, fn, ...args) { 
  for (let i = 0; i < n; i++) 
    fn(...args, i + 1) 
}

function test(x, i) { console.log(x + i) }
loop(5, test, "This is loop ")
Enter fullscreen mode Exit fullscreen mode

Demo

Collapse
 
artydev profile image
artydev • Edited

Hy Eckehard

Here is a possible answer.
If I find something clearer i will post it

const test = (x, i) =>  console.log(x + i) 

function _loop (n, fn, args) {
  [...Array(n)].forEach((_, i) => fn([...args].join(''), i + 1))
}

_loop(6, test, "This is a test ");

console.log("_____________________________")

const loop = curry(_loop);

loop(5, test, "this is a test ");

loop(5, test)("this is a test ");

loop(5)(test)("this is a test ");

loop(5)(test, "this is a test ");

Enter fullscreen mode Exit fullscreen mode

Demo

Collapse
 
efpage profile image
Eckehard • Edited

Hy,

I´m not sure this works: fn([...args].join('')? And you can call it loop(), as there is no loop keyword in JS. Anyway, a nice option.

Here are 3 working versions

function loop1(n, fn, ...args) {
    for (let i = 0; i < n; i++)
        fn(...args, i + 1)
}

function loop2(n, fn, ...args) {
    if (n > 1) 
        loop2(n - 1, fn, ...args)
    fn(...args, n)
}


function _loop(n, fn, ...args) {
    [...Array(n)].forEach((_, i) => fn(...args, i + 1))
}

function test(x, y, i) { console.log(x, y * i) }

loop1(3, test, "This is loop 1:", 10); console.log("-----------")
loop2(3, test, "This is loop 2:", 10); console.log("-----------")
_loop(3, test, "This is loop 3:", 10); console.log("-----------")
Enter fullscreen mode Exit fullscreen mode

Demo

Thread Thread
 
artydev profile image
artydev

Eckehard, have you clicked on the démo link?
That works perfectly

Thread Thread
 
artydev profile image
artydev • Edited

Those functions can t be curried
I really liké the recursion way

Thread Thread
 
efpage profile image
Eckehard

The Demo works, but only with a single string parameter. Neither multiple arguments nor a numerical parameter seem to work.

Thread Thread
 
artydev profile image
artydev • Edited

And this version ? :
Thank you for the suggestions.

Demo

Thread Thread
 
efpage profile image
Eckehard

In my understanding, loop should be a shortcut for "for". So it should not be too specific. The most common use would be:

function loop(n, fn, ...args) {
    if (n > 1) loop(n - 1, fn, ...args)
    fn(...args, n)
}

loop(5, i => {
    console.log(i)
});

This is easy to read and reasonable shorter than for( ; ; )

Adding ...args is a nice option to call functions from within loop, as there is no other way to do this. If you write `loop(5,fn(x))`, fn would only be evaluated once. Using `loop(5, fn, x)` will call fn five times as expected.

loop should be able to pass all kinds of parameters to the function. So, you should try if your version works with all possible parameters. 
Enter fullscreen mode Exit fullscreen mode
Thread Thread
 
artydev profile image
artydev

I agree with this seems the most elegant way.

And if one wish all sort of variations is possible :

function loop(n, fn, ...args) {
    if (n > 1) loop(n - 1, fn, ...args)
    fn(...args)
}

loop(5, console.log, 1,2,3)

const loopn= (n) => (fn, ...args) => {
     if (n > 1) loop(n - 1, fn, ...args)
    fn(...args)
}

let loop5 = loopn(5);

loop5(console.log, "a test")

const loopnf = (n, fn) => (...args) => {
       if (n > 1) loop(n - 1, fn, ...args)
    fn(...args)
}

let loop3logconsole = loopnf(3, console.log)

loop3logconsole("TEST");
Enter fullscreen mode Exit fullscreen mode

Demo

Thread Thread
 
efpage profile image
Eckehard

Cool, just don´t forget to call fn(...args, n), otherwise you cannot access the index from within your function.

And you should decide, if the index counts from from 1 to n (which is intuitive) or from 0 to n-1, which makes it easier to deal with array indices.

Happy coding!

Thread Thread
 
artydev profile image
artydev

:-) Thank you Eckehard

Collapse
 
artydev profile image
artydev

Hy Eckehard,
I published a answer
Regards