// Define the "filter map" operator.
interface Operator<T, U> {
	( value: T, index: number, values: T[] ) : U;
}
 
// If we want to augment one of the global / native modules, like Array, we have to use
// the special "global" module reference.
declare global {
	// If we want to add INSTANCE METHODS to one of the native classes, we have
	// to "declaration merge" an interface into the existing class.
	interface Array<T> {
		filterMap<T, U>( operator: Operator<T, U>, context?: any ) : U[];
	}
}
 
// I map the contextual collection onto another collection by appending defined results
// of the operation onto the mapped collection. This means that "undefined" results will
// be omitted from the mapped collection.
// --
// CAUTION: Augmentations for the global scope can only be directly nested in external
// modules or ambient module declarations. As such, we are EXPORTING this function to
// force this file to become an "external module" (one that imports or exports other
// modules).
export function filterMap<T, U>( operator: Operator<T, U>, context: any = null ) : U[] {
 
	var results = this.reduce(
		( reduction: U[], value: T, index: number, values: T[] ) : U[] => {
 
			var mappedValue = operator.call( context, value, index, values );
 
			if ( mappedValue !== undefined ) {
 
				reduction.push( mappedValue );
 
			}
 
			return( reduction );
 
		},
		[]
	);
 
	return( results );
 
};
 
// Protect against conflicting definitions. Since each module in Node.js is evaluated
// only once (and then cached in memory), we should never hit this line more than once
// (no matter how many times we include it). As such, the only way this method would
// already be defined is if another module has injected it without knowing that this
// module would follow-suit.
if ( Array.prototype.filterMap ) {
 
	throw( new Error( "Array.prototype.filterMap is already defined - overriding it will be dangerous." ) );
 
}
 
// Augment the global Array prototype (ie, add an instance method).
Array.prototype.filterMap = filterMap;