{"id":13806910,"url":"https://github.com/DockYard/ember-composable-helpers","last_synced_at":"2025-05-13T23:33:35.233Z","repository":{"id":4112171,"uuid":"52030740","full_name":"DockYard/ember-composable-helpers","owner":"DockYard","description":"Composable helpers for declarative templating in Ember","archived":false,"fork":false,"pushed_at":"2024-05-21T12:11:01.000Z","size":3812,"stargazers_count":633,"open_issues_count":48,"forks_count":126,"subscribers_count":11,"default_branch":"master","last_synced_at":"2025-04-13T19:50:04.327Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/DockYard.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-02-18T18:57:08.000Z","updated_at":"2025-04-04T12:48:00.000Z","dependencies_parsed_at":"2024-06-18T12:19:07.031Z","dependency_job_id":"cec21bcc-2fd3-4d46-bf1e-804ffb79e4e5","html_url":"https://github.com/DockYard/ember-composable-helpers","commit_stats":{"total_commits":317,"total_committers":72,"mean_commits":4.402777777777778,"dds":0.7823343848580442,"last_synced_commit":"62fb18314e563d70c6588d733df4d26fd4b14aa1"},"previous_names":["dockyard/ember-functional-helpers"],"tags_count":76,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DockYard%2Fember-composable-helpers","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DockYard%2Fember-composable-helpers/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DockYard%2Fember-composable-helpers/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DockYard%2Fember-composable-helpers/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DockYard","download_url":"https://codeload.github.com/DockYard/ember-composable-helpers/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254043218,"owners_count":22004912,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-04T01:01:17.827Z","updated_at":"2025-05-13T23:33:30.201Z","avatar_url":"https://github.com/DockYard.png","language":"JavaScript","funding_links":[],"categories":["Packages","Addons / Support"],"sub_categories":["Helpers","Awesome Addons"],"readme":"# ember-composable-helpers\n![Download count all time](https://img.shields.io/npm/dt/ember-composable-helpers.svg) [![CircleCI](https://circleci.com/gh/DockYard/ember-composable-helpers.svg?style=shield)](https://circleci.com/gh/DockYard/ember-composable-helpers) [![npm version](https://badge.fury.io/js/ember-composable-helpers.svg)](https://badge.fury.io/js/ember-composable-helpers) [![Ember Observer Score](http://emberobserver.com/badges/ember-composable-helpers.svg)](http://emberobserver.com/addons/ember-composable-helpers)\n\n**[ember-composable-helpers is built and maintained by DockYard, contact us for expert Ember.js consulting](https://dockyard.com/ember-consulting)**.\n\nComposable helpers for Ember that enables more declarative templating. These helpers can be _composed_ together to form powerful ideas:\n\n```hbs\n{{#each (map-by \"fullName\" users) as |fullName|}}\n  \u003cinput type=\"text\" value={{fullName}} onchange={{action (mut newName)}}\u003e\n  \u003cbutton {{action (pipe updateFullName saveUser) newName}}\u003e\n    Update and save {{fullName}} to {{newName}}\n  \u003c/button\u003e\n{{/each}}\n```\n\nTo install:\n\n**Ember 3.13+:**\n\n```no-highlight\nember install ember-composable-helpers\n```\n\n**Ember 3.12 and below:**\n\n```no-highlight\nember install ember-composable-helpers@^2.4.0\n```\n\nWatch a free video overview presented by EmberMap:\n\n\u003ca href='https://embermap.com/topics/refactorings/ember-composable-helpers'\u003e\n  \u003cimg height=\"20\" src=\"https://frontend.embermap.com/assets/images/logo-7333e9d5f48c9cd4a0ee6476a5af1083.png\"\u003e\n\u003c/a\u003e\n\n## Table of Contents\n  - [Configuration](#configuration)\n  - [Argument ordering](#argument-ordering)\n  - [Upgrade Guide](#upgrade-guide)\n  - [Available helpers](#available-helpers)\n    - [Action helpers](#action-helpers)\n      - [`call`](#call)\n      - [`pipe`](#pipe)\n      - [`compute`](#compute)\n      - [`toggle`](#toggle)\n      - [`noop`](#noop)\n      - [`optional`](#optional)\n      - [`queue`](#queue)\n    - [Array helpers](#array-helpers)\n      - [`map`](#map)\n      - [`map-by`](#map-by)\n      - [`sort-by`](#sort-by)\n      - [`filter`](#filter)\n      - [`filter-by`](#filter-by)\n      - [`reject-by`](#reject-by)\n      - [`find-by`](#find-by)\n      - [`intersect`](#intersect)\n      - [`invoke`](#invoke)\n      - [`union`](#union)\n      - [`take`](#take)\n      - [`drop`](#drop)\n      - [`reduce`](#reduce)\n      - [`repeat`](#repeat)\n      - [`reverse`](#reverse)\n      - [`range`](#range)\n      - [`join`](#join)\n      - [`compact`](#compact)\n      - [`includes`](#includes)\n      - [`append`](#append)\n      - [`chunk`](#chunk)\n      - [`without`](#without)\n      - [`shuffle`](#shuffle)\n      - [`flatten`](#flatten)\n      - [`object-at`](#object-at)\n      - [`slice`](#slice)\n      - [`next`](#next)\n      - [`has-next`](#has-next)\n      - [`previous`](#previous)\n      - [`has-previous`](#has-previous)\n    - [Object helpers](#object-helpers)\n      - [`entries`](#entries)\n      - [`from-entries`](#from-entries)\n      - [`group-by`](#group-by)\n      - [`keys`](#keys)\n      - [`pick`](#pick)\n      - [`values`](#values)\n    - [Math helpers](#math-helpers)\n      - [`inc`](#inc)\n      - [`dec`](#dec)\n    - [String helpers](#string-helpers)\n    - [See also:](#see-also)\n  - [Legal](#legal)\n  - [Contributors](#contributors)\n\n## Configuration\nIf you don't need all the helpers, you can specify which to include or remove from your build using `only` or `except` within your `ember-cli-build.js`:\n\n```js\nmodule.exports = function(defaults) {\n  var app = new EmberApp(defaults, {\n    'ember-composable-helpers': {\n      only: ['inc', 'dec', 'pipe'],\n      except: ['filter-by']\n    }\n  });\n```\n\nBoth `only` and `except` can be safely used together (the addon computes the diff), although it's best if you only use one for your own sanity.\n\n```js\nexcept: ['pipe'] // imports all helpers except `pipe`\nonly: ['pipe'] // imports only `pipe`\n```\n\n## Argument ordering\n\nThis addon is built with _composability_ in mind, and in order to faciliate that,\nthe ordering of arguments is somewhat different then you might be used to.\n\nFor all non-unary helpers, the subject of the helper function will always be the last argument.\nThis way the arguments are better readable if you compose together multiple helpers:\n\n```hbs\n{{take 5 (sort-by \"lastName\" \"firstName\" (filter-by \"active\" array))}}\n```\n\nFor action helpers, this will mean better currying semantics:\n\n```hbs\n\u003cbutton {{action (pipe (action \"closePopover\") (toggle \"isExpanded\")) this}}\u003e\n  {{if isExpanded \"I am expanded\" \"I am not\"}}\n\u003c/button\u003e\n```\n\n## Upgrade Guide\nFor help upgrading between major versions, check out the [upgrading documentation](https://github.com/DockYard/ember-composable-helpers/blob/master/UPGRADING.md).\n\n## Available helpers\n\n### Action helpers\n\n#### `pipe`\nPipes the return values of actions in a sequence of actions. This is useful to compose a pipeline of actions, so each action can do only one thing.\n\n```hbs\n\u003cbutton {{action (pipe (action 'addToCart') (action 'purchase') (action 'redirectToThankYouPage')) item}}\u003e\n  1-Click Buy\n\u003c/button\u003e\n```\n\nThe `pipe` helper is Promise-aware, meaning that if any action in the pipeline returns a Promise, its return value will be piped into the next action. If the Promise rejects, the rest of the pipeline will be aborted.\n\nThe `pipe` helper can also be used directly as a closure action (using `pipe-action`) when being passed into a Component, which provides an elegant syntax for composing actions:\n\n```hbs\n{{foo-bar\n    addAndSquare=(pipe-action (action \"add\") (action \"square\"))\n    multiplyAndSquare=(pipe-action (action \"multiply\") (action \"square\"))\n}}\n```\n\n```hbs\n{{! foo-bar/template.hbs }}\n\u003cbutton {{action addAndSquare 2 4}}\u003eAdd and Square\u003c/button\u003e\n\u003cbutton {{action multiplyAndSquare 2 4}}\u003eMultiply and Square\u003c/button\u003e\n```\n\n**[⬆️️ back to top](#table-of-contents)**\n\n#### `call`\nCalls the given function with arguments\n\n```hbs\n{{#each (call (fn this.callMeWith @daysInMonth) as |week|}}\n  {{#each week as |day|}}\n    {{day}}\n  {{/each}}\n{{/each}}\n```\n\n**[⬆️ back to top](#table-of-contents)**\n\n\n#### `compute`\nCalls an action as a template helper.\n\n```hbs\nThe square of 4 is {{compute (action \"square\") 4}}\n```\n\n**[⬆️ back to top](#table-of-contents)**\n\n#### `toggle`\nToggles a boolean value.\n\n```hbs\n\u003cbutton {{action (toggle \"isExpanded\" this)}}\u003e\n  {{if isExpanded \"I am expanded\" \"I am not\"}}\n\u003c/button\u003e\n```\n\n`toggle` can also be used directly as a closure action using `toggle-action`:\n\n```hbs\n{{foo-bar\n    toggleIsExpanded=(toggle-action \"isExpanded\" this)\n    toggleIsSelected=(toggle-action \"isSelected\" this)\n}}\n```\n\n```hbs\n{{! foo-bar/template.hbs }}\n\u003cbutton {{action toggleIsExpanded}}\u003eOpen / Close\u003c/button\u003e\n\u003cbutton {{action toggleIsSelected}}\u003eSelect / Deselect\u003c/button\u003e\n```\n\n`toggle` also accepts optional values to rotate through:\n\n```hbs\n\u003cbutton {{action (toggle \"currentName\" this \"foo\" \"bar\" \"baz\")}}\u003e\n  {{currentName}}\n\u003c/button\u003e\n```\n\n**[⬆️ back to top](#table-of-contents)**\n\n#### `noop`\n\nReturns an empty function.\n\n```hbs\n\u003cdiv {{on \"mouseenter\" (if @isLoading (noop) @sendTrackingEvent))}}\u003eSome content\u003c/div\u003e\n```\n\n**[⬆️ back to top](#table-of-contents)**\n\n#### `optional`\n\nAllows for the passed in action to not exist.\n\n```hbs\n\u003cbutton {{action (optional handleClick)}}\u003eClick Me\u003c/button\u003e\n```\n\n**[⬆️ back to top](#table-of-contents)**\n\n#### `queue`\n\nLike `pipe`, this helper runs actions in a sequence (from left-to-right). The\ndifference is that this helper passes the original arguments to each action, not\nthe result of the previous action in the sequence.\n\nIf one of the actions in the sequence returns a promise, then it will wait for\nthat promise to resolve before calling the next action in the sequence. If a\npromise is rejected it will stop the sequence and no further actions will be\ncalled.\n\n```hbs\n\u003cbutton {{action (queue (action \"backupData\") (action \"unsafeOperation\") (action \"restoreBackup\"))}} /\u003e\n```\n\n**[⬆️ back to top](#table-of-contents)**\n\n---\n\n### Array helpers\n\n#### `map`\nMaps a callback on an array.\n\n```hbs\n{{#each (map (action \"getName\") users) as |fullName|}}\n  {{fullName}}\n{{/each}}\n```\n\n**[⬆️ back to top](#table-of-contents)**\n\n#### `map-by`\nMaps an array on a property.\n\n```hbs\n{{#each (map-by \"fullName\" users) as |fullName|}}\n  {{fullName}}\n{{/each}}\n```\n\n#### `sort-by`\nSort an array by given properties.\n\n```hbs\n{{#each (sort-by \"lastName\" \"firstName\" users) as |user|}}\n  {{user.lastName}}, {{user.firstName}}\n{{/each}}\n```\n\nYou can append `:desc` to properties to sort in reverse order.\n\n```hbs\n{{#each (sort-by \"age:desc\" users) as |user|}}\n    {{user.firstName}} {{user.lastName}} ({{user.age}})\n{{/each}}\n```\n\nYou can also pass a method as the first argument:\n\n```hbs\n{{#each (sort-by (action \"mySortAction\") users) as |user|}}\n  {{user.firstName}} {{user.lastName}} ({{user.age}})\n{{/each}}\n```\n\n**[⬆️ back to top](#table-of-contents)**\n\n\n#### `filter`\nFilters an array by a callback.\n\n```hbs\n{{#each (filter (action \"isActive\") users) as |user|}}\n  {{user.name}} is active!\n{{/each}}\n```\n\n#### `filter-by`\nFilters an array by a property.\n\n```hbs\n{{#each (filter-by \"isActive\" true users) as |user|}}\n  {{user.name}} is active!\n{{/each}}\n```\n\nIf you omit the second argument it will test if the property is truthy.\n\n```hbs\n{{#each (filter-by \"address\" users) as |user|}}\n  {{user.name}} has an address specified!\n{{/each}}\n```\n\nYou can also pass an action as second argument:\n\n```hbs\n{{#each (filter-by \"age\" (action \"olderThan\" 18) users) as |user|}}\n  {{user.name}} is older than eighteen!\n{{/each}}\n```\n\n**[⬆️ back to top](#table-of-contents)**\n\n#### `reject-by`\nThe inverse of filter by.\n\n```hbs\n{{#each (reject-by \"isActive\" true users) as |user|}}\n  {{user.name}} is not active!\n{{/each}}\n```\n\nIf you omit the third argument it will test if the property is falsey.\n\n```hbs\n{{#each (reject-by \"address\" users) as |user|}}\n  {{user.name}} does not have an address specified!\n{{/each}}\n```\n\nYou can also pass an action as third argument:\n\n```hbs\n{{#each (reject-by \"age\" (action \"youngerThan\" 18) users) as |user|}}\n  {{user.name}} is older than eighteen!\n{{/each}}\n```\n\n**[⬆️ back to top](#table-of-contents)**\n\n#### `find-by`\nReturns the first entry matching the given value.\n\n```hbs\n{{#with (find-by 'name' lookupName people) as |person|}}\n  {{#if person}}\n    {{#link-to 'person' person}}\n      Click here to see {{person.name}}'s details\n    {{/link-to}}\n  {{/if}}\n{{/with}}\n```\n\n**[⬆️ back to top](#table-of-contents)**\n\n#### `intersect`\nCreates an array of unique values that are included in all given arrays.\n\n```hbs\n\u003ch1\u003eMatching skills\u003c/h1\u003e\n{{#each (intersect desiredSkills currentSkills) as |skill|}}\n  {{skill.name}}\n{{/each}}\n```\n\n**[⬆️ back to top](#table-of-contents)**\n\n#### `invoke`\nInvokes a method on an object, or on each object of an array.\n\n```hbs\n\u003cdiv id=\"popup\"\u003e\n  {{#each people as |person|}}\n    \u003cbutton {{action (invoke \"rollbackAttributes\" person)}}\u003e\n      Undo\n    \u003c/button\u003e\n  {{/each}}\n  \u003ca {{action (invoke \"save\" people)}}\u003eSave\u003c/a\u003e\n\u003c/div\u003e\n```\n\n**[⬆️ back to top](#table-of-contents)**\n\n#### `union`\n\nJoins arrays to create an array of unique values. When applied to a single array, has the same behavior as `uniq`.\n\n```hbs\n{{#each (union cartA cartB cartC) as |cartItem|}}\n  {{cartItem.price}} x {{cartItem.quantity}} for {{cartItem.name}}\n{{/each}}\n```\n\n**[⬆️ back to top](#table-of-contents)**\n\n#### `take`\nReturns the first `n` entries of a given array.\n\n```hbs\n\u003ch3\u003eTop 3:\u003c/h3\u003e\n{{#each (take 3 contestants) as |contestant|}}\n  {{contestant.rank}}. {{contestant.name}}\n{{/each}}\n```\n\n**[⬆️ back to top](#table-of-contents)**\n\n#### `drop`\nReturns an array with the first `n` entries omitted.\n\n```hbs\n\u003ch3\u003eOther contestants:\u003c/h3\u003e\n{{#each (drop 3 contestants) as |contestant|}}\n  {{contestant.rank}}. {{contestant.name}}\n{{/each}}\n```\n\n**[⬆️ back to top](#table-of-contents)**\n\n#### `reduce`\nReduce an array to a value.\n\n```hbs\n{{reduce (action \"sum\") 0 (array 1 2 3)}}\n```\n\nThe last argument is initial value. If you omit it, undefined will be used.\n\n#### `repeat`\nRepeats `n` times. This can be useful for making an n-length arbitrary list for iterating upon (you can think of this form as a times helper, a la Ruby's `5.times { ... }`):\n\n```hbs\n{{#each (repeat 3) as |empty|}}\n  I will be rendered 3 times\n{{/each}}\n```\n\nYou can also give it a value to repeat:\n\n```hbs\n{{#each (repeat 3 \"Adam\") as |name|}}\n  {{name}}\n{{/each}}\n```\n\n**[⬆️ back to top](#table-of-contents)**\n\n#### `reverse`\nReverses the order of the array.\n\n```hbs\n{{#each (reverse friends) as |friend|}}\n  If {{friend}} was first, they are now last.\n{{/each}}\n```\n\n**[⬆️ back to top](#table-of-contents)**\n\n#### `range`\nGenerates a range of numbers between a `min` and `max` value.\n\n```hbs\n{{#each (range 10 20) as |number|}}\n  {{! `number` will go from 10 to 19}}\n{{/each}}\n```\n\nIt can also be set to `inclusive`:\n\n```hbs\n{{#each (range 10 20 true) as |number|}}\n  {{! `number` will go from 10 to 20}}\n{{/each}}\n```\n\nAnd works with a negative range:\n\n```hbs\n{{#each (range 20 10) as |number|}}\n  {{! `number` will go from 20 to 11}}\n{{/each}}\n```\n\n**[⬆️ back to top](#table-of-contents)**\n\n#### `join`\nJoins the given array with an optional separator into a string.\n\n```hbs\n{{join ', ' categories}}\n```\n\n**[⬆️ back to top](#table-of-contents)**\n\n#### `compact`\nRemoves blank items from an array.\n\n```hbs\n{{#each (compact arrayWithBlanks) as |notBlank|}}\n  {{notBlank}} is most definitely not blank!\n{{/each}}\n```\n\n**[⬆️ back to top](#table-of-contents)**\n\n#### `includes`\nChecks if a given value or sub-array is included within an array.\n\n```hbs\n{{includes selectedItem items}}\n{{includes 1234 items}}\n{{includes \"First\" (w \"First Second Third\") }}\n{{includes (w \"First Second\") (w \"First Second Third\")}}\n```\n\n**[⬆️ back to top](#table-of-contents)**\n\n#### `append`\nAppends the given arrays and/or values into a single flat array.\n\n```hbs\n{{#each (append catNames dogName) as |petName|}}\n  {{petName}}\n{{/each}}\n```\n\n**[⬆️ back to top](#table-of-contents)**\n\n#### `chunk`\nReturns the given array split into sub-arrays the length of the given value.\n\n```hbs\n{{#each (chunk 7 daysInMonth) as |week|}}\n  {{#each week as |day|}}\n    {{day}}\n  {{/each}}\n{{/each}}\n```\n\n**[⬆️ back to top](#table-of-contents)**\n\n#### `without`\nReturns the given array without the given item(s).\n\n```hbs\n{{#each (without selectedItem items) as |remainingItem|}}\n  {{remainingItem.name}}\n{{/each}}\n```\n\n**[⬆️ back to top](#table-of-contents)**\n\n#### `shuffle`\nShuffles an array with a randomizer function, or with `Math.random` as a default. Your randomizer function should return a number between 0 and 1.\n\n```hbs\n{{#each (shuffle array) as |value|}}\n  {{value}}\n{{/each}}\n```\n\n```hbs\n{{#each (shuffle (action \"myRandomizer\") array) as |value|}}\n  {{value}}\n{{/each}}\n```\n\n**[⬆️ back to top](#table-of-contents)**\n\n#### `flatten`\nFlattens an array to a single dimension.\n\n```hbs\n{{#each (flatten anArrayOfNamesWithMultipleDimensions) as |name|}}\n  Name: {{name}}\n{{/each}}\n```\n\n**[⬆️ back to top](#table-of-contents)**\n\n#### `object-at`\nReturns the object at the given index of an array.\n\n```hbs\n{{object-at index array}}\n```\n\n**[⬆️ back to top](#table-of-contents)**\n\n#### `slice`\nSlices an array\n\n```hbs\n{{#each (slice 1 3 array) as |value|}}\n  {{value}}\n{{/each}}\n```\n\n**[⬆️ back to top](#table-of-contents)**\n\n#### `next`\nReturns the next element in the array given the current element. **Note:** Accepts an optional boolean\nparameter, `useDeepEqual`, to flag whether a deep equal comparison should be performed.\n\n```hbs\n\u003cbutton onclick={{action (mut selectedItem) (next selectedItem useDeepEqual items)}}\u003eNext\u003c/button\u003e\n```\n\n**[⬆️ back to top](#table-of-contents)**\n\n#### `has-next`\nChecks if the array has an element after the given element. **Note:** Accepts an optional boolean\nparameter, `useDeepEqual`, to flag whether a deep equal comparison should be performed.\n\n```hbs\n{{#if (has-next page useDeepEqual pages)}}\n  \u003cbutton\u003eNext\u003c/button\u003e\n{{/if}}\n```\n\n**[⬆️ back to top](#table-of-contents)**\n\n#### `previous`\nReturns the previous element in the array given the current element. **Note:** Accepts an optional boolean\nparameter, `useDeepEqual`, to flag whether a deep equal comparison should be performed.\n\n```hbs\n\u003cbutton onclick={{action (mut selectedItem) (previous selectedItem useDeepEqual items)}}\u003ePrevious\u003c/button\u003e\n```\n\n**[⬆️ back to top](#table-of-contents)**\n\n#### `has-previous`\nChecks if the array has an element before the given element. **Note:** Accepts an optional boolean\nparameter, `useDeepEqual`, to flag whether a deep equal comparison should be performed\n\n```hbs\n{{#if (has-previous page useDeepEqual pages)}}\n  \u003cbutton\u003ePrevious\u003c/button\u003e\n{{/if}}\n```\n\n**[⬆️ back to top](#table-of-contents)**\n\n---\n\n### Object helpers\n\n#### `entries`\nReturns an array of a given object's own enumerable string-keyed property `[key, value]` pairs\n\n```hbs\n  {{#each (entries object) as |entry|}}\n    {{get entry 0}}:{{get entry 1}}\n  {{/each}}\n```\n\nYou can pair it with other array helpers too. For example\n\n```hbs\n  {{#each (sort-by myOwnSortByFunction (entries myObject)) as |entry|}}\n    {{get entry 0}}\n  {{/each}}`);\n```\n\n**[⬆️ back to top](#table-of-contents)**\n\n#### `from-entries`\nConverts a two-dimensional array of `[key, value]` pairs into an Object\n\n```hbs\n  {{#each-in (from-entries entries) as |key value|}}\n    {{key}}:{{value}}\n  {{/each}}\n```\n\nYou can pair it with other array helpers too. For example, to copy only\nproperties with non-falsey values:\n\n```hbs\n  {{#each-in (from-entries (filter-by \"1\" (entries myObject))) as |k v|}}\n    {{k}}: {{v}}\n  {{/each-in}}`);\n```\n\n**[⬆️ back to top](#table-of-contents)**\n\n#### `group-by`\nReturns an object where the keys are the unique values of the given property, and the values are an array with all items of the array that have the same value of that property.\n\n```hbs\n{{#each-in (group-by \"category\" artists) as |category artists|}}\n  \u003ch3\u003e{{category}}\u003c/h3\u003e\n  \u003cul\u003e\n    {{#each artists as |artist|}}\n      \u003cli\u003e{{artist.name}}\u003c/li\u003e\n    {{/each}}\n  \u003c/ul\u003e\n{{/each-in}}\n```\n\n**[⬆️ back to top](#table-of-contents)**\n\n#### `keys`\nReturns an array of keys of given object.\n\n```hbs\n{{#with (keys fields) as |labels|}}\n  \u003ch3\u003eThis article contain {{labels.length}} fields\u003c/h3\u003e\n  \u003cul\u003e\n    {{#each labels as |label|}}\n      \u003cli\u003e{{label}}\u003c/li\u003e\n    {{/each}}\n  \u003c/ul\u003e\n{{/with}}\n```\n\n**[⬆️ back to top](#table-of-contents)**\n\n#### `pick`\nReceives an object and picks a specified path off of it to pass on. Intended for use with `{{on}}` modifiers placed on form elements.\n\n```hbs\n  \u003cinput\n    ...\n    {{on 'input' (pipe (pick 'target.value') this.onInput)}}\n  /\u003e\n```\n\nIt also supports an optional second argument to make common usage more ergonomic.\n\n```hbs\n  \u003cinput\n    ...\n    {{on 'input' (pick 'target.value' this.onInput)}}\n  /\u003e\n```\n\n**[⬆️ back to top](#table-of-contents)*\n\n#### `values`\nReturns an array of values from the given object.\n\n```hbs\n{{#with (values fields) as |data|}}\n  \u003ch3\u003eThis article contain {{data.length}} fields\u003c/h3\u003e\n  \u003cul\u003e\n    {{#each data as |datum|}}\n      \u003cli\u003e{{datum}}\u003c/li\u003e\n    {{/each}}\n  \u003c/ul\u003e\n{{/with}}\n```\n\n**[⬆️ back to top](#table-of-contents)*\n\n---\n\n### Math helpers\n\n#### `inc`\nIncrements by `1` or `step`.\n\n```hbs\n{{inc numberOfPeople}}\n{{inc 2 numberOfPeople}}\n```\n\n**[⬆️ back to top](#table-of-contents)**\n\n#### `dec`\nDecrements by `1` or `step`.\n\n```hbs\n{{dec numberOfPeople}}\n{{dec 2 numberOfPeople}}\n```\n\n**[⬆️ back to top](#table-of-contents)**\n\n---\n\n### String helpers\n\nString helpers were extracted to the [ember-cli-string-helpers](https://github.com/romulomachado/ember-cli-string-helpers) addon.\n\n### See also:\n\n* [ember-truth-helpers](https://github.com/jmurphyau/ember-truth-helpers)\n* [ember-math-helpers](https://github.com/shipshapecode/ember-math-helpers)\n* [ember-cli-string-helpers](https://github.com/romulomachado/ember-cli-string-helpers)\n\n## Legal\n\n[DockYard](http://dockyard.com/ember-consulting), Inc \u0026copy; 2016\n\n[@dockyard](http://twitter.com/dockyard)\n\n[Licensed under the MIT license](http://www.opensource.org/licenses/mit-license.php)\n\n## Contributors\n\nWe're grateful to these wonderful contributors who've contributed to `ember-composable-helpers`:\n\n[//]: contributor-faces\n\u003ca href=\"https://github.com/poteto\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/1390709?v=4\" title=\"poteto\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/martndemus\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/903637?v=4\" title=\"martndemus\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/snewcomer\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/7374640?v=4\" title=\"snewcomer\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/marten-dockyard\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/40497337?v=4\" title=\"marten-dockyard\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/romulomachado\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/266400?v=4\" title=\"romulomachado\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/cibernox\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/265339?v=4\" title=\"cibernox\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/vikram7\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/7072744?v=4\" title=\"vikram7\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/Asherlc\"\u003e\u003cimg src=\"https://avatars3.githubusercontent.com/u/1294552?v=4\" title=\"Asherlc\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/ef4\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/319282?v=4\" title=\"ef4\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/gmurphey\"\u003e\u003cimg src=\"https://avatars3.githubusercontent.com/u/373721?v=4\" title=\"gmurphey\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/GavinJoyce\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/2526?v=4\" title=\"GavinJoyce\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/jrjohnson\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/349624?v=4\" title=\"jrjohnson\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/thomascchen\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/11340361?v=4\" title=\"thomascchen\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/xomaczar\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/2079865?v=4\" title=\"xomaczar\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/bcardarella\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/18524?v=4\" title=\"bcardarella\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/leizhao4\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/763847?v=4\" title=\"leizhao4\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/maabernethy\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/4601777?v=4\" title=\"maabernethy\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/robbiespeed\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/1743161?v=4\" title=\"robbiespeed\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/spencer516\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/2889608?v=4\" title=\"spencer516\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/taras\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/74687?v=4\" title=\"taras\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/blimmer\"\u003e\u003cimg src=\"https://avatars1.githubusercontent.com/u/630449?v=4\" title=\"blimmer\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/bwittenbrook3\"\u003e\u003cimg src=\"https://avatars3.githubusercontent.com/u/5309252?v=4\" title=\"bwittenbrook3\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/chrislopresto\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/93691?v=4\" title=\"chrislopresto\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/makepanic\"\u003e\u003cimg src=\"https://avatars3.githubusercontent.com/u/1205444?v=4\" title=\"makepanic\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/ConorLinehan\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/5772795?v=4\" title=\"ConorLinehan\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/Daniel-Xu\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/548144?v=4\" title=\"Daniel-Xu\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/HeroicEric\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/602204?v=4\" title=\"HeroicEric\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/brzpegasus\"\u003e\u003cimg src=\"https://avatars1.githubusercontent.com/u/1691398?v=4\" title=\"brzpegasus\" width=\"80\" height=\"80\"\u003e\u003c/a\u003e\n\n[//]: contributor-faces\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FDockYard%2Fember-composable-helpers","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FDockYard%2Fember-composable-helpers","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FDockYard%2Fember-composable-helpers/lists"}