Skip to content

Commit

Permalink
Added shuffle helper
Browse files Browse the repository at this point in the history
- Per issue #20
  • Loading branch information
robbiespeed authored and poteto committed Apr 27, 2016
1 parent ae03424 commit 02214fa
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 0 deletions.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ For action helpers, this will mean better currying semantics:
+ [`chunk`](#chunk)
+ [`without`](#without)
+ [`array`](#array)
+ [`shuffle`](#shuffle)
* [Object](#object-helpers)
+ [`group-by`](#group-by)
* [Math](#math-helpers)
Expand Down Expand Up @@ -553,6 +554,23 @@ Similar to the `hash` helper, this lets you compose arrays directly in the templ

**[⬆️ back to top](#available-helpers)**

#### `shuffle`
Shuffles an array with a randomizer function, or with `Math.random` as a default. Your randomizer function should return a number between 0 and 1.

```hbs
{{#each (shuffle array) as |value|}}
{{value}}
{{/each}}
```

```hbs
{{#each (shuffle array (action "myRandomizer")) as |value|}}
{{value}}
{{/each}}
```

**[⬆️ back to top](#available-helpers)**

---

### Object helpers
Expand Down
39 changes: 39 additions & 0 deletions addon/helpers/shuffle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import {
A as emberArray,
isEmberArray as isArray
} from 'ember-array/utils';
import Helper from 'ember-helper';
import observer from 'ember-metal/observer';
import set from 'ember-metal/set';
import { typeOf } from 'ember-utils';

export function shuffle(array, randomizer) {
array = array.slice(0);
let count = array.length;
let rand, temp;
randomizer = (typeOf(randomizer) === 'function' && randomizer) || Math.random;

while (count > 1) {
rand = Math.floor(randomizer() * (count--));

temp = array[count];
array[count] = array[rand];
array[rand] = temp;
}
return array;
}

export default Helper.extend({
compute([array, random]) {
if (!isArray(array)) {
return emberArray([array]);
}

set(this, 'array', array);
return shuffle(array, random);
},

arrayContentDidChange: observer('array.[]', function() {
this.recompute();
})
});
1 change: 1 addition & 0 deletions addon/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export { default as PipeActionHelper } from './helpers/pipe-action';
export { default as RangeHelper } from './helpers/range';
export { default as RejectByHelper } from './helpers/reject-by';
export { default as RepeatHelper } from './helpers/repeat';
export { default as ShuffleHelper } from './helpers/shuffle';
export { default as SortByHelper } from './helpers/sort-by';
export { default as TakeHelper } from './helpers/take';
export { default as ToggleHelper } from './helpers/toggle';
Expand Down
1 change: 1 addition & 0 deletions app/helpers/shuffle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default, shuffle } from 'ember-composable-helpers/helpers/shuffle';
102 changes: 102 additions & 0 deletions tests/integration/helpers/shuffle-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
import Ember from 'ember';

const { A: emberArray, run } = Ember;

moduleForComponent('shuffle', 'Integration | Helper | {{shuffle}}', {
integration: true
});

test('It shuffles array', function(assert) {
this.set('array', emberArray([1, 2]));
this.render(hbs`
{{~#each (shuffle array) as |value|~}}
{{value}}
{{~/each~}}
`);

let shuffled = this.$().text().trim();
assert.ok(shuffled === '12' || shuffled === '21', 'array is shuffled');
});

test('It shuffles array using passed in randomizer', function(assert) {
this.set('array', emberArray([1, 2, 3, 4]));
this.on('fake', () => 0);
this.render(hbs`
{{~#each (shuffle array (action "fake")) as |value|~}}
{{value}}
{{~/each~}}
`);

assert.equal(this.$().text().trim(), '2341', 'array is shuffled');
});

test('It handles a non-ember array', function(assert) {
this.set('array', [1, 2, 3, 4]);
this.on('fake', () => 0);
this.render(hbs`
{{~#each (shuffle array (action "fake")) as |value|~}}
{{value}}
{{~/each~}}
`);

assert.equal(this.$().text().trim(), '2341', 'array is shuffled');
});

test('It does not mutate the original array', function(assert) {
this.set('array', emberArray([1, 2, 3, 4]));
this.on('fake', () => 0);
this.render(hbs`
{{~#each (shuffle array (action "fake")) as |value|~}}
{{value}}
{{~/each~}}
`);

assert.equal(this.$().text().trim(), '2341', 'array is shuffled');
assert.deepEqual(this.get('array'), [1, 2, 3, 4], 'the original array is not shuffled');
});

test('It gracefully handles non-array values', function(assert) {
this.set('notArray', 1);
this.render(hbs`
{{~#each (shuffle notArray) as |value|~}}
{{value}}
{{~/each~}}
`);

assert.equal(this.$().text().trim(), '1', 'the non array value is rendered');
});

test('It recomputes the shuffle if the array changes', function(assert) {
this.set('array', emberArray([1, 2, 3, 4]));
this.on('fake', () => 0);
this.render(hbs`
{{~#each (shuffle array (action "fake")) as |value|~}}
{{value}}
{{~/each~}}
`);

assert.equal(this.$().text().trim(), '2341', 'array is shuffled');

this.set('array', emberArray(['a', 2, 3, 4]));

assert.equal(this.$().text().trim(), '234a', 'array is shuffled');
});

test('It recomputes the shuffle if an item in the array changes', function(assert) {
let array = emberArray([1, 2, 3, 4]);
this.set('array', array);
this.on('fake', () => 0);
this.render(hbs`
{{~#each (shuffle array (action "fake")) as |value|~}}
{{value}}
{{~/each~}}
`);

assert.equal(this.$().text().trim(), '2341', 'array is shuffled');

run(() => array.replace(2, 1, [5]));

assert.equal(this.$().text().trim(), '2541', 'array is shuffled');
});

0 comments on commit 02214fa

Please sign in to comment.