emberjs/ember.js — pull request #2711 by hjdivad (2013-05-21 22:36:12+00:00)
Title: Add `Ember.reduceComputed`, `Ember.arrayComputed`.

Reduce computed properties and computed properties that reduce an enumerable.
Array computed properties are reduce computed properties whose value happens
to be an array.

Reduce computed properties use one-at-a-time semantics to make computations
from arrays pleasant to write without making it easy to introduce O(n²)
runtime for something that only requires O(n).

They can be chained together and retain their O(n) semantics.

A simple example:

```js
var obj = Ember.Object.createWithMixins({
items: Ember.A([{
fname: "Jaime", lname: "Lannister", age: 34
}, {
fname: "Cersei", lname: "Lannister", age: 34
}, {
fname: "Robb", lname: "Stark", age: 16
}, {
fname: "Bran", lname: "Stark", age: 8
}]),

lannisters: Ember.computed.filterProperty('items', 'lname',
'Lannister'),
lannisterSorting: Ember.A(['fname']),
sortedLannisters: Ember.computed.sort('lannisters', 'lannisterSorting'),

starks: Ember.computed.filterProperty('items', 'lname', 'Stark'),
starkAges: Ember.computed.mapProperty('starks', 'age'),
oldestStarkAge: Ember.computed.max('starkAges')
});

var items = obj.get('items');
// each computed property will have to process only Tyrion to update, rather
// than having to look through all of `items` again.
items.pushObject(Ember.Object.create({
fname: "Tyrion", lname: "Lannister", age: 26
}));
```

Sugar functions include:
- Ember.computed.max
- Ember.computed.min
- Ember.computed.map
- Ember.computed.mapProperty
- Ember.computed.filter
- Ember.computed.filterProperty
- Ember.computed.uniq
- Ember.computed.intersect
- Ember.computed.setDiff
- Ember.computed.sort

They are all implemented with an easily used primitive. The implementation
of
`Ember.computed.min` serves as an example:

```js
Ember.computed.min = function (dependentKey) {
return Ember.reduceComputed.call(null, dependentKey, {
initialValue: Infinity,

addedItem: function (accumulatedValue, item, changeMeta, instanceMeta)
{
return Math.min(accumulatedValue, item);
},

removedItem: function (accumulatedValue, item, changeMeta,
instanceMeta) {
if (item > accumulatedValue) {
return accumulatedValue;
}
}
});
};
```

cc @wagenet