consta=[10,20,20,10,10,30,50,10,20]// [[10, 10, 10, 10], [20, 20, 20], [30], [50]]// => 3 complete pairs (& 3 odd socks)constgroup=(p,c)=>(cinp)?{...p,[c]:p[c]+1}:{...p,[c]:1}// Note: ~~ is Math.floor in js.constsumPairs=(acc,x)=>acc+~~(x/2)// Note: .map can be used for function composition provided you wrap the input ary in an array => [ary].// for further info Visit https://egghead.io/lessons/javascript-linear-data-flow-with-container-style-types-boxconstcompletePairs=(n,ary)=>[ary].map(a=>a.reduce(group,{})).map(Object.values).map(a=>a.reduce(sumPairs,0)).pop()console.log(completePairs(9,a))
Hello John, I have definitely enjoyed reading your post and have learned a few nice tricks from it. My issue though with the solution will be readability first. It really takes a minute to grasp what is going on here. The second one is that a lot of transforms (map chain) are performed here where they probably could be taken care of by one computation reducing the complexity and improving the performance. Running against a simple benchmark this solution is over 300% slower than the proposed one.
I will host a webinar probably next week that will focus on why this second approach might be more suitable for this type of challenges. It will mean the world to me if you could attend and add to the discussion
I will take a closer look and since you donβt like to be put on the spot π I think I want to have our discussion transcend the comments section with an article that captures the exchange.
In the meantime Iβll run a benchmark on it but as far as readability this seems easier to digest IMHO.
I have started to write in a more functional way and isolating the arity for flexible composition. I think in the end it comes down to choosing the right tool and style for the right job.
I recognize that my solution must look a bit strange on first sight. I was reading on MDN recently about the draft proposal for the pipeline operator aka |> to be added to JavaScript(as yet unsupported).
I also had passed sometime on the egghead.io site
re: egghead.io/courses/professor-frisb... and was intrigued how .map is used here to produce a topdown workflow composing(or piping) one function's result to the next function. What a shame that, as you have discovered, JS is not performant in this way, because, I for one, think this is an elegant and clean workflow that I hope, one day, will be worth using.
Of course speed of execution is always an important issue and so for the moment, atleast, we fall back to the roots of JavaScript and use it's for, for of, while, etc loops. Shame, because I really do like a more declarative style of coding and here I was merely experimenting.
Perhaps you will forgive a quick hack to make my solution a tad more readable(see code below) by placing a pipe method on the Array prototype. Sorry, I can't do anything about JS performance, but I think somewhat less than 10,000 socks to sort would be favourable; that sock merchant needs to keep his house in closer order I think!
Incidentally, please excuse me if I decline the webinar participation; it really is not my thing and you may take this reply as my input to that.
Have a great day.
if(!Array.prototype.pipe)Array.prototype.pipe=Array.prototype.mapconstreduce=(fn,init)=>xs=>Array.prototype.reduce.call(xs,fn,init)consta=[10,20,20,10,10,30,50,10,20]// [[10, 10, 10, 10], [20, 20, 20], [30], [50]]// => 3 complete pairs (& 3 odd socks)constgroup=(p,c)=>(cinp)?{...p,[c]:p[c]+1}:{...p,[c]:1}// Note: ~~ is Math.floor in js.constsumPairs=(acc,x)=>acc+~~(x/2)// Note: .pipe is function composition. Pipes the result of a function to the next.// Visit https://egghead.io/lessons/javascript-linear-data-flow-with-container-style-types-boxconstcompletePairs=(n,ary)=>[ary].pipe(reduce(group,{})).pipe(Object.values).pipe(reduce(sumPairs,0)).pop()console.log(completePairs(9,a))// => 3
As a final request, I would be interested in what benchmark timings (in milliseconds) you achieved for my soloution vs your solution for a test array of 10,000 integers.
Thanks, I did and borrowing your concept I found a reducing callback that works, but I think it is going to show what Ady said about functional code in JS with reference execution speed. Array.prototype.reduce is slow compared to other JS imperative looping constructs. However, I am getting somewhere around 30ms for the code below using console.time on a 10k set.
When put in perspective; a blink of an eye is approx 300 - 400ms, roughly a third of a second, so it's still pretty quick.
Hey John awesome to have you back and yet coming up with other ways of enriching the discussion. I really, really like what you are proposing here.
It really boils down to having an imperative or a declarative style in solving those problems. Your solution here is kind a mix of both. For those who are used to reduce this might look declarative even though the ternary operation is leaning more towards imperative.
Regarding performance, I will quote what I think is a real gem from the above article:
Many (if not all) declarative approaches have some sort of underlying imperative abstraction.
-- Tyler McGinnis
That should settle the performance discussion since declarative (map, reduce, filter, etc...) will always include and abstract one or more imperative layers such as a for loop. I can live with 30ms for a dataset of 10,000 items though if I have the understanding that the for loop might cut in half, it is an informed decision.
The last part that is left from my perspective would be readability. I think it is highly readable for the trained eye but might still be hard to digest for the enthused and upcoming at first glance. With time though, one might learn to grow and appreciate the details in it.
Thanks Ady, Tyler's article is interesting and he also says in that same article...
"Imperative: C, C++, Java
Declarative: SQL, HTML
(Can Be) Mix: JavaScript, C#, Python"
JavaScript, as he points out, is a mixed bag and I think we should take every opportunity to use that wherever it works best. Yes, I have been having fun trying out different solutions.
But, I think, that's all from me on the sock merchant. I had a thought that I might go and learn a functional programming language like Elm or Elixir.
Thanks again for the Sock Merchant problem... it has been fun!
An alternative solution:
Hello John, I have definitely enjoyed reading your post and have learned a few nice tricks from it. My issue though with the solution will be readability first. It really takes a minute to grasp what is going on here. The second one is that a lot of transforms (map chain) are performed here where they probably could be taken care of by one computation reducing the complexity and improving the performance. Running against a simple benchmark this solution is over 300% slower than the proposed one.
I will host a webinar probably next week that will focus on why this second approach might be more suitable for this type of challenges. It will mean the world to me if you could attend and add to the discussion
A somewhat faster solution.
My hope is that this is more readable and fast enough to satisfy most users.
I will take a closer look and since you donβt like to be put on the spot π I think I want to have our discussion transcend the comments section with an article that captures the exchange.
In the meantime Iβll run a benchmark on it but as far as readability this seems easier to digest IMHO.
I have started to write in a more functional way and isolating the arity for flexible composition. I think in the end it comes down to choosing the right tool and style for the right job.
Hello Ady, thanks for the reply.
I recognize that my solution must look a bit strange on first sight. I was reading on MDN recently about the draft proposal for the pipeline operator aka |> to be added to JavaScript(as yet unsupported).
I also had passed sometime on the egghead.io site
re: egghead.io/courses/professor-frisb... and was intrigued how .map is used here to produce a topdown workflow composing(or piping) one function's result to the next function. What a shame that, as you have discovered, JS is not performant in this way, because, I for one, think this is an elegant and clean workflow that I hope, one day, will be worth using.
Of course speed of execution is always an important issue and so for the moment, atleast, we fall back to the roots of JavaScript and use it's for, for of, while, etc loops. Shame, because I really do like a more declarative style of coding and here I was merely experimenting.
Perhaps you will forgive a quick hack to make my solution a tad more readable(see code below) by placing a pipe method on the Array prototype. Sorry, I can't do anything about JS performance, but I think somewhat less than 10,000 socks to sort would be favourable; that sock merchant needs to keep his house in closer order I think!
Incidentally, please excuse me if I decline the webinar participation; it really is not my thing and you may take this reply as my input to that.
Have a great day.
As a final request, I would be interested in what benchmark timings (in milliseconds) you achieved for my soloution vs your solution for a test array of 10,000 integers.
Looks complicated. Take a look at my comment.
Thanks, I did and borrowing your concept I found a reducing callback that works, but I think it is going to show what Ady said about functional code in JS with reference execution speed. Array.prototype.reduce is slow compared to other JS imperative looping constructs. However, I am getting somewhere around 30ms for the code below using console.time on a 10k set.
When put in perspective; a blink of an eye is approx 300 - 400ms, roughly a third of a second, so it's still pretty quick.
Hey John awesome to have you back and yet coming up with other ways of enriching the discussion. I really, really like what you are proposing here.
It really boils down to having an imperative or a declarative style in solving those problems. Your solution here is kind a mix of both. For those who are used to reduce this might look declarative even though the ternary operation is leaning more towards imperative.
If those concepts are new or not clear, I can't recommend enough Tyle McGinnis article on the subject: Imperative vs Declarative Programming
Regarding performance, I will quote what I think is a real gem from the above article:
That should settle the performance discussion since declarative (map, reduce, filter, etc...) will always include and abstract one or more imperative layers such as a for loop. I can live with 30ms for a dataset of 10,000 items though if I have the understanding that the for loop might cut in half, it is an informed decision.
The last part that is left from my perspective would be readability. I think it is highly readable for the trained eye but might still be hard to digest for the enthused and upcoming at first glance. With time though, one might learn to grow and appreciate the details in it.
Cheers
Thanks Ady, Tyler's article is interesting and he also says in that same article...
"Imperative: C, C++, Java
Declarative: SQL, HTML
(Can Be) Mix: JavaScript, C#, Python"
JavaScript, as he points out, is a mixed bag and I think we should take every opportunity to use that wherever it works best. Yes, I have been having fun trying out different solutions.
But, I think, that's all from me on the sock merchant. I had a thought that I might go and learn a functional programming language like Elm or Elixir.
Thanks again for the Sock Merchant problem... it has been fun!
You will be missed Sir John, thank you and happy coding :)