{"action":"opened","issue":{"url":"https://api.github.com/repos/google/closure-compiler/issues/2828","repository_url":"https://api.github.com/repos/google/closure-compiler","labels_url":"https://api.github.com/repos/google/closure-compiler/issues/2828/labels{/name}","comments_url":"https://api.github.com/repos/google/closure-compiler/issues/2828/comments","events_url":"https://api.github.com/repos/google/closure-compiler/issues/2828/events","html_url":"https://github.com/google/closure-compiler/issues/2828","id":299861446,"number":2828,"title":"[request] expand simple reductions into loops","user":{"login":"alexchandel","id":637714,"avatar_url":"https://avatars3.githubusercontent.com/u/637714?v=4","gravatar_id":"","url":"https://api.github.com/users/alexchandel","html_url":"https://github.com/alexchandel","followers_url":"https://api.github.com/users/alexchandel/followers","following_url":"https://api.github.com/users/alexchandel/following{/other_user}","gists_url":"https://api.github.com/users/alexchandel/gists{/gist_id}","starred_url":"https://api.github.com/users/alexchandel/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/alexchandel/subscriptions","organizations_url":"https://api.github.com/users/alexchandel/orgs","repos_url":"https://api.github.com/users/alexchandel/repos","events_url":"https://api.github.com/users/alexchandel/events{/privacy}","received_events_url":"https://api.github.com/users/alexchandel/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":0,"created_at":"2018-02-23T21:50:24Z","updated_at":"2018-02-23T21:50:24Z","closed_at":null,"author_association":"NONE","body":"A common, functional way to count the number of occurrences (or the number of elements that satisfy a certain predicate) in an array is to use the `reduce` method like so:\r\n\r\n```js\r\nvar total = array.reduce((a, e) => a + (e === 7), 0)\r\n```\r\n\r\nThis might be abstracted into a function, like:\r\n```js\r\n/**\r\n * @param {Array<*>} array\r\n * @param {function(*): boolean} predicate\r\n * @return {boolean}\r\n */\r\nfunction count (array, predicate) {\r\n  return array.reduce((a, e) => a + predicate(e), 0)\r\n}\r\n```\r\n\r\nAll good and readable. The problem is this is about 6-7 times slower than a loop when both are fully inlined (tested in jsbench):\r\n```js\r\n/**\r\n * @param {Array<*>} array\r\n * @param {function(*): boolean} predicate\r\n * @return {boolean}\r\n */\r\nfunction count (array, predicate) {\r\n  var total = 0\r\n  for ( var i = array.length; i--; ) {\r\n    total += predicate(array[i])\r\n  }\r\n  return total\r\n}\r\n```\r\n\r\nThe performance advantage falls to about 2-3:1 if the predicate can't be inlined at compile time. This would seem to make `Array.prototype.reduce` a prime candidate for optimization, when its predicate in known at compile time.\r\n\r\n## Optimization pattern:\r\n\r\nAny reduction of the form:\r\n\r\n```js\r\nresult = array.reduce(nonDeletingCombination), initial)\r\n```\r\n\r\ncan be transformed into:\r\n\r\n```js\r\nvar result = initial\r\nfor (let i = array.length, m = array.length - 1; i--; ) {\r\n  result = nonDeletingCombination(result, array[m - i])\r\n}\r\n```\r\n\r\nwithout loss of generality for a 6x speed boost. The only requirement for preserving `reduce`'s behavior is that `nonDeletingCombination` not delete elements from the array, as this would result in  `undefined` being passed to it rather than early termination. Such cases can be optimized instead to:\r\n\r\n```js\r\nvar result = initial\r\nfor (let i = 0; i < array.length; i++) {\r\n  result = anyCombination(result, array[i])\r\n}\r\n```\r\n\r\nIf `nonDeletingCombination(accumulator, element)` is of the form `nonDeletingCombination(accumulator, predicate(element))` then the binary assignment operator could be inserted as well."}}
{"actor":{"display_login":"alexchandel"}}