Skip to content

Instantly share code, notes, and snippets.

@alexdiliberto
Last active May 11, 2018 10:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save alexdiliberto/8285278 to your computer and use it in GitHub Desktop.
Save alexdiliberto/8285278 to your computer and use it in GitHub Desktop.
Ember.computed.flattenArray()
// ember-runtime/lib/computed/reduce_computed_macros.js
// Credit to Nopik via https://github.com/emberjs/ember.js/pull/3503
/*
var item1 = Ember.Object.create({
tags: [ 'important', 'bug', 'task' ],
});
var item2 = Ember.Object.create({
tags: [ 'urgent', 'feature', 'task' ],
});
App.ItemList = Ember.ArrayProxy.extend({
allTags: Ember.computed.flattenArray( 'content', 'tags' )
});
var myList = App.ItemList.create({ content: [ item1, item2 ] });
myList.get( 'allTags' );
// -> ["important", "bug", "task", "urgent", "feature", "task"]
*/
/**
A computed property which takes an array of arrays, flatten single
level of them and returns the result.
Optionally, it can take a list of objects, get a property from
each object (which is expected to be an array) and return one
array being a concatenation of those partial ones.
Example
```javascript
var item1 = Ember.Object.create({
tags: [ 'important', 'bug', 'task' ],
});
var item2 = Ember.Object.create({
tags: [ 'urgent', 'feature', 'task' ],
});
App.ItemList = Ember.ArrayProxy.extend({
allTags: Ember.computed.flattenArray( 'content', 'tags' )
});
var myList = App.ItemList.create({ content: [ item1, item2 ] });
myList.get( 'allTags' );
// -> ["important", "bug", "task", "urgent", "feature", "task"]
```
To use without a key:
```javascript
App.ItemList = Ember.ArrayProxy.extend({
tagMap: Ember.computed.mapBy( 'content', 'tags' );
allTags: Ember.computed.flattenArray( 'tagMap' )
});
var myList = App.ItemList.create({ content: [ item1, item2 ] });
myList.get( 'allTags' );
// -> ["important", "bug", "task", "urgent", "feature", "task"]
```
@method computed.flattenArray
@for Ember
@param {String} sourceArrayProperty
@param {String} (optional) if given, values from sourceArray are
treated as objects, and property named by the `key` will be extracted
from them. If `key` is not given, the values from sourceArray are
expected to be the arrays to be flattened.
@return {Ember.ComputedProperty} computes a new flattened array from
the input
*/
Ember.computed.flattenArray = function(nestedArray, key) {
return Ember.arrayComputed(nestedArray, {
initialize: function(array, changeMeta, instanceMeta) {
instanceMeta.lengths = [];
instanceMeta.listeners = [];
return array;
},
addedItem: function(array, item, changeMeta, instanceMeta) {
var args, flat, i, len, listener, localIndex;
if (item != null) {
if (key != null) {
flat = item.get( key );
} else {
flat = item;
}
} else {
flat = null;
}
if (flat != null) {
listener = Ember.Object.create({
flat: flat,
arrayWillChange: function() {},
arrayDidChange: function(source_list, start, remove_count, add_count) {
var args, i, idx, lidx, rem_amt, source_slice, len;
idx = 0;
lidx = 0;
for (i = 0, len = instanceMeta.listeners.length; i < len; i++) {
if (instanceMeta.listeners[i] === this) {
break;
}
lidx += 1;
idx += instanceMeta.lengths[i];
}
source_slice = [];
if (add_count < 0) {
source_slice = source_list.slice(0);
} else {
if (add_count > 0) {
source_slice = source_list.slice(start, start + add_count);
}
}
rem_amt = 0;
if (remove_count < 0) {
rem_amt = instanceMeta.lengths[lidx];
} else {
rem_amt = remove_count;
}
instanceMeta.lengths[lidx] += source_slice.length - rem_amt;
args = [ idx + start, rem_amt ].concat( source_slice );
array.arrayContentWillChange(idx + start, rem_amt, source_slice.length);
array.splice.apply(array, args);
array.arrayContentDidChange(idx + start, rem_amt, source_slice.length);
}
});
flat.addArrayObserver(listener);
}
localIndex = 0;
for (i = 0; i < changeMeta.index; i++ ) {
localIndex += instanceMeta.lengths[i];
}
if (flat != null) {
len = flat.get('length');
} else {
len = 0;
}
instanceMeta.lengths.splice(changeMeta.index, 0, len);
instanceMeta.listeners.splice(changeMeta.index, 0, listener);
args = [localIndex, 0];
if (flat != null) {
args = args.concat(flat);
}
array.arrayContentWillChange(localIndex, 0, len);
array.splice.apply(array, args);
array.arrayContentDidChange(localIndex, 0, len);
return array;
},
removedItem: function(array, item, changeMeta, instanceMeta) {
var i, listener, localIndex, old_len, flat;
localIndex = 0;
for (i = 0; i < changeMeta.index; i++ ) {
localIndex += instanceMeta.lengths[i];
}
old_len = instanceMeta.lengths[changeMeta.index];
instanceMeta.lengths.splice(changeMeta.index, 1);
listener = instanceMeta.listeners[changeMeta.index];
if (listener != null) {
if ((flat = listener.get('flat')) != null) {
flat.removeArrayObserver(listener);
}
}
instanceMeta.listeners.splice(changeMeta.index, 1);
array.arrayContentWillChange(localIndex, old_len, 0);
array.splice(localIndex, old_len);
array.arrayContentDidChange(localIndex, old_len, 0);
return array;
}
});
};
@lolmaus
Copy link

lolmaus commented Oct 16, 2015

Can you make this into an addon?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment