Add _.transform.

Former-commit-id: 6c040fedd130e8436ff99b1d70892ac8cebbb996
This commit is contained in:
John-David Dalton
2013-05-18 19:12:22 -07:00
parent 3721db34ab
commit 9270cc47b5
4 changed files with 130 additions and 24 deletions

View File

@@ -88,7 +88,7 @@
'contains': ['indexOf', 'isString'],
'countBy': ['createCallback', 'forEach'],
'createCallback': ['identity', 'isEqual', 'keys'],
'debounce': [],
'debounce': ['isObject'],
'defaults': ['isArray', 'keys'],
'defer': ['bind'],
'delay': [],
@@ -163,9 +163,10 @@
'sortedIndex': ['createCallback', 'identity'],
'tap': ['value'],
'template': ['defaults', 'escape', 'keys', 'values'],
'throttle': [],
'throttle': ['isObject'],
'times': ['createCallback'],
'toArray': ['isString', 'values'],
'transform': ['createCallback', 'forOwn', 'isArray', 'isObject'],
'unescape': [],
'union': ['isArray', 'uniq'],
'uniq': ['createCallback', 'indexOf'],
@@ -276,6 +277,7 @@
'parseInt',
'partialRight',
'runInContext',
'transform',
'unzip'
];
@@ -800,7 +802,7 @@
* @returns {String} Returns the `isArguments` fallback.
*/
function getIsArgumentsFallback(source) {
return (source.match(/(?:\s*\/\/.*)*\n( *)if *\((?:!support\.argsClass|!isArguments)[\s\S]+?};\n\1}/) || [''])[0];
return (source.match(/(?:\s*\/\/.*)*\n( *)if *\((?:!support\.argsClass|!isArguments)[\s\S]+?\n *};\n\1}/) || [''])[0];
}
/**
@@ -824,7 +826,18 @@
* @returns {String} Returns the `isFunction` fallback.
*/
function getIsFunctionFallback(source) {
return (source.match(/(?:\s*\/\/.*)*\n( *)if *\(isFunction\(\/x\/[\s\S]+?};\n\1}/) || [''])[0];
return (source.match(/(?:\s*\/\/.*)*\n( *)if *\(isFunction\(\/x\/[\s\S]+?\n *};\n\1}/) || [''])[0];
}
/**
* Gets the `createObject` fallback from `source`.
*
* @private
* @param {String} source The source to inspect.
* @returns {String} Returns the `isArguments` fallback.
*/
function getCreateObjectFallback(source) {
return (source.match(/(?:\s*\/\/.*)*\n( *)if *\((?:!nativeCreate)[\s\S]+?\n *};\n\1}/) || [''])[0];
}
/**
@@ -1069,6 +1082,17 @@
return source.replace(getIsFunctionFallback(source), '');
}
/**
* Removes the `createObject` fallback from `source`.
*
* @private
* @param {String} source The source to process.
* @returns {String} Returns the modified source.
*/
function removeCreateObjectFallback(source) {
return source.replace(getCreateObjectFallback(source), '');
}
/**
* Removes the `Object.keys` object iteration optimization from `source`.
*
@@ -1939,11 +1963,11 @@
source = removeSupportProp(source, 'fastBind');
source = replaceSupportProp(source, 'argsClass', 'false');
_.each(['getPrototypeOf', 'nativeBind', 'nativeIsArray', 'nativeKeys'], function(varName) {
_.each(['getPrototypeOf'], function(varName) {
source = replaceVar(source, varName, 'false');
});
_.each(['isIeOpera', 'isV8', 'nativeBind', 'nativeIsArray', 'nativeKeys', 'reNative'], function(varName) {
_.each(['isIeOpera', 'isV8', 'nativeBind', 'nativeCreate', 'nativeIsArray', 'nativeKeys', 'reNative'], function(varName) {
source = removeVar(source, varName);
});
@@ -1957,6 +1981,23 @@
return match.replace(/\bnativeIsArray\s*\|\|\s*/, '');
});
// replace `createObject` and `isArguments` with their fallbacks
_.each({
'createObject': { 'get': getCreateObjectFallback, 'remove': removeCreateObjectFallback },
'isArguments': { 'get': getIsArgumentsFallback, 'remove': removeIsArgumentsFallback }
},
function(util, methodName) {
source = source.replace(matchFunction(source, methodName).replace(RegExp('[\\s\\S]+?function ' + methodName), ''), function() {
var snippet = util.get(source),
body = snippet.match(RegExp(methodName + ' *= *function([\\s\\S]+?\\n *});'))[1],
indent = getIndent(snippet);
return body.replace(RegExp('^' + indent, 'gm'), indent.slice(0, -2)) + '\n';
});
source = util.remove(source);
});
// replace `_.keys` with `shimKeys`
source = source.replace(
matchFunction(source, 'keys').replace(/[\s\S]+?var keys *= */, ''),
@@ -1964,17 +2005,6 @@
);
source = removeFunction(source, 'shimKeys');
// replace `_.isArguments` with fallback
source = source.replace(matchFunction(source, 'isArguments').replace(/[\s\S]+?function isArguments/, ''), function() {
var fallback = getIsArgumentsFallback(source),
body = fallback.match(/isArguments *= *function([\s\S]+? *});/)[1],
indent = getIndent(fallback);
return body.replace(RegExp('^' + indent, 'gm'), indent.slice(0, -2)) + '\n';
});
source = removeIsArgumentsFallback(source);
}
if (isModern) {
source = removeSupportSpliceObjects(source);
@@ -1987,6 +2017,7 @@
else {
source = removeIsArrayFallback(source);
source = removeIsFunctionFallback(source);
source = removeCreateObjectFallback(source);
// remove `shimIsPlainObject` from `_.isPlainObject`
source = source.replace(matchFunction(source, 'isPlainObject'), function(match) {

View File

@@ -222,6 +222,7 @@
'times',
'toArray',
'trailing',
'transform',
'unescape',
'unindexedChars',
'union',

View File

@@ -198,6 +198,7 @@
/* Native method shortcuts for methods with the same name as other `lodash` methods */
var nativeBind = reNative.test(nativeBind = toString.bind) && nativeBind,
nativeCreate = reNative.test(nativeCreate = Object.create) && nativeCreate,
nativeIsArray = reNative.test(nativeIsArray = Array.isArray) && nativeIsArray,
nativeIsFinite = context.isFinite,
nativeIsNaN = context.isNaN,
@@ -264,8 +265,8 @@
* `invoke`, `keys`, `map`, `max`, `memoize`, `merge`, `min`, `object`, `omit`,
* `once`, `pairs`, `partial`, `partialRight`, `pick`, `pluck`, `push`, `range`,
* `reject`, `rest`, `reverse`, `shuffle`, `slice`, `sort`, `sortBy`, `splice`,
* `tap`, `throttle`, `times`, `toArray`, `union`, `uniq`, `unshift`, `unzip`,
* `values`, `where`, `without`, `wrap`, and `zip`
* `tap`, `throttle`, `times`, `toArray`, `transform`, `union`, `uniq`, `unshift`,
* `unzip`, `values`, `where`, `without`, `wrap`, and `zip`
*
* The non-chainable wrapper functions are:
* `clone`, `cloneDeep`, `contains`, `escape`, `every`, `find`, `has`,
@@ -771,9 +772,7 @@
}
if (this instanceof bound) {
// ensure `new bound` is an instance of `func`
noop.prototype = func.prototype;
thisBinding = new noop;
noop.prototype = null;
thisBinding = createObject(func.prototype);
// mimic the constructor's `return` behavior
// http://es5.github.com/#x13.2.2
@@ -839,6 +838,28 @@
);
}
/**
* Creates a new object with the specified `prototype`.
*
* @private
* @param {Object} prototype The prototype object.
* @returns {Object} Returns the new object.
*/
function createObject(prototype) {
return isObject(prototype) ? nativeCreate(prototype) : {};
}
// fallback for browsers without `Object.create`
if (!nativeCreate) {
var createObject = function(prototype) {
if (isObject(prototype)) {
noop.prototype = prototype;
var result = new noop;
noop.prototype = null;
}
return result || {};
};
}
/**
* Used by `template` to escape characters for inclusion in compiled
* string literals.
@@ -2278,6 +2299,56 @@
return result;
}
/**
* Transforms an `object` to an new `accumulator` object which is the result
* of running each of its elements through the `callback`, with each `callback`
* execution potentially mutating the `accumulator` object. The `callback`is
* bound to `thisArg` and invoked with four arguments; (accumulator, value, key, object).
* Callbacks may exit iteration early by explicitly returning `false`.
*
* @static
* @memberOf _
* @category Objects
* @param {Array|Object} collection The collection to iterate over.
* @param {Function} [callback=identity] The function called per iteration.
* @param {Mixed} [accumulator] The custom accumulator value.
* @param {Mixed} [thisArg] The `this` binding of `callback`.
* @returns {Mixed} Returns the accumulated value.
* @example
*
* var squares = _.transform([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], function(result, num) {
* num *= num;
* if (num % 2) {
* return result.push(num) < 3;
* }
* });
* // => [1, 9, 25]
*
* var mapped = _.transform({ 'a': 1, 'b': 2, 'c': 3 }, function(result, num, key) {
* result[key] = num * 3;
* });
* // => { 'a': 3, 'b': 6, 'c': 9 }
*/
function transform(object, callback, accumulator, thisArg) {
var isArr = isArray(object);
callback = lodash.createCallback(callback, thisArg, 4);
if (arguments.length < 3) {
if (isArr) {
accumulator = [];
} else {
var ctor = object && object.constructor,
proto = ctor && ctor.prototype;
accumulator = createObject(proto);
}
}
(isArr ? each : forOwn)(object, function(value, index, object) {
return callback(accumulator, value, index, object);
});
return accumulator;
}
/**
* Creates an array composed of the own enumerable property values of `object`.
*
@@ -4547,7 +4618,7 @@
if (options === true) {
var leading = true;
trailing = false;
} else if (options && objectTypes[typeof options]) {
} else if (isObject(options)) {
leading = options.leading;
trailing = 'trailing' in options ? options.trailing : trailing;
}
@@ -4776,7 +4847,7 @@
}
if (options === false) {
leading = false;
} else if (options && objectTypes[typeof options]) {
} else if (isObject(options)) {
leading = 'leading' in options ? options.leading : leading;
trailing = 'trailing' in options ? options.trailing : trailing;
}
@@ -5384,6 +5455,7 @@
lodash.throttle = throttle;
lodash.times = times;
lodash.toArray = toArray;
lodash.transform = transform;
lodash.union = union;
lodash.uniq = uniq;
lodash.unzip = unzip;

View File

@@ -225,6 +225,7 @@
'omit',
'pairs',
'pick',
'transform',
'values'
];
@@ -316,6 +317,7 @@
'parseInt',
'partialRight',
'runInContext',
'transform',
'unzip'
];