I'm transitioning from relying on jQuery to building apps in AngularJS. It's recommended in a number of places to not mix jQuery and Angular code.
One thing I miss though is the jQuery $.map function for arrays. I know this could be re-written using the native Javascript map function, but this is not implemented in all browsers (notably, IE < v9).
So, is there an Angular equivalent, or should I got back to writing for (var x = 0; x < foo; x += 1) {...} so I can stop including jQuery?
UPDATE Sometimes knowing what to search for is all you need. Bergie says 'look for polyfills'. Here's a reference guide (from the Modernizr crew) for a bunch of resources for making modern code work on older browsers: HTML5 Cross Browser Polyfills
Check here: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/map
Mozilla has supplied an Array.map polyfill for unsupported browsers
if (!Array.prototype.map) {
Array.prototype.map = function(callback, thisArg) {
var T, A, k;
if (this == null) {
throw new TypeError(" this is null or not defined");
}
// 1. Let O be the result of calling ToObject passing the |this| value as the argument.
var O = Object(this);
// 2. Let lenValue be the result of calling the Get internal method of O with the argument "length".
// 3. Let len be ToUint32(lenValue).
var len = O.length >>> 0;
// 4. If IsCallable(callback) is false, throw a TypeError exception.
// See: http://es5.github.com/#x9.11
if (typeof callback !== "function") {
throw new TypeError(callback + " is not a function");
}
// 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
if (thisArg) {
T = thisArg;
}
// 6. Let A be a new array created as if by the expression new Array(len) where Array is
// the standard built-in constructor with that name and len is the value of len.
A = new Array(len);
// 7. Let k be 0
k = 0;
// 8. Repeat, while k < len
while(k < len) {
var kValue, mappedValue;
// a. Let Pk be ToString(k).
// This is implicit for LHS operands of the in operator
// b. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk.
// This step can be combined with c
// c. If kPresent is true, then
if (k in O) {
// i. Let kValue be the result of calling the Get internal method of O with argument Pk.
kValue = O[ k ];
// ii. Let mappedValue be the result of calling the Call internal method of callback
// with T as the this value and argument list containing kValue, k, and O.
mappedValue = callback.call(T, kValue, k, O);
// iii. Call the DefineOwnProperty internal method of A with arguments
// Pk, Property Descriptor {Value: mappedValue, : true, Enumerable: true, Configurable: true},
// and false.
// In browsers that support Object.defineProperty, use the following:
// Object.defineProperty(A, Pk, { value: mappedValue, writable: true, enumerable: true, configurable: true });
// For best browser support, use the following:
A[ k ] = mappedValue;
}
// d. Increase k by 1.
k++;
}
// 9. return A
return A;
};
}
No, there is no equivalent in Angular. And as you discovered, no you don't need to fall back to writing imperative code.
Instead, just use the map function that is built directly into JavaScript. If you need to support IE8, insert the polyfill at the beginning of your scripts.
Related
looking for an explanation on this line of code. I understand arrow functions to a degree. The purpose of this code snippet/challenge is; "Given any number of parameters, return true if none of the arguments are falsy." I've seen the solution like this:
const nothingIsNothing = (...args) => args.every(x => x)
Examples of arguments and the expected results are:
nothingIsNothing(0, false, undefined, null) ➞ false
nothingIsNothing(33, "Hello", true, []) ➞ true
nothingIsNothing(true, false) ➞ false
I just don't understand how the section (x => x) evaluates to either truthy or falsy. Can someone explain how this works? I hope this makes sense lol. Thanks!
With .every, if any of the return values from the callback are falsey, the .every evaluates to false, otherwise it evaluates to true. So x => x as a callback means: take every value in the array and return it immediately. If all are truthy, the whole .every evaluates to true, else false.
It's doing the same logic as this:
const nothingIsNothing = (...args) => {
for (const arg of args) {
if (!arg) return false;
}
return true;
};
Or, implementing something similar to .every yourself:
// don't do this in real code, this is just an example
Array.prototype.myEvery = function(callback) {
for (const item of this) {
if (!callback(item)) return false;
}
return true;
};
console.log([1, 2, 3].myEvery(x => x));
console.log([1, 2, 3, 0].myEvery(x => x));
Its a combinations of a couple of things
Javascript implicit return statement.
getVal = a => a;
is the same as
function getVal(a) { return a }
every web API method run until it encounters a falsy (not false) value. Below is a quote from MDN.
The every method executes the provided callback function once for each
element present in the array until it finds the one where callback
returns a falsy value. If such an element is found, the every method
immediately returns false. Otherwise, if callback returns a truthy
value for all elements, every returns true.
param => param is the same as (param) => { return param }
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
These are all the falsy values in JavaScript: https://developer.mozilla.org/en-US/docs/Glossary/Falsy
The documentation for the return value of every states the following:
true if the callback function returns a truthy value for every array
element. Otherwise, false.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every
So something like this will be false as not all elements are truthy
const x = ['test', 0, null, -0].every(el => el);
console.log(x);
But something like this will return true as all elements are truthy values
const x = ['test', 1, 'hi', 10].every(el => el);
console.log(x);
As per the docs the every() method tests whether all elements in the array pass the test implemented by the provided function. It returns a Boolean value i.e true if the callback function returns a truthy value for every array element. Otherwise, false.
const nothingIsNothing = (...args) => args.every(x=>x) can be expanded to:
const nothingIsNothing = (...args) => args.every(function(x){
if(x)
return true
else
return false
})
Here is another version which will help you understand the shorthand better. The value x is typecasted to a boolean and true/false is returned.
const nothingIsNothing = (...args) => args.every(Boolean)
console.log(nothingIsNothing(0, false, undefined, null))
console.log(nothingIsNothing(33, "Hello", true, []))
The Array.prototpe.every() does the heavy lifting here.
The every() method tests whether all elements in the array pass the
test implemented by the provided function. It returns a Boolean value.
The PolyFil following the ECMA-262, 5th specification, assuming Object and TypeError have their original values, and that callbackfn.call evaluates to the original value of Function.prototype.call, demonstrate the internal behavior nicely:
Array.prototype.PolyFillEvery = function(callbackfn, thisArg) {
'use strict';
var T, k;
if (this == null) {
throw new TypeError('this is null or not defined');
}
// 1. Let O be the result of calling ToObject passing the this
// value as the argument.
var O = Object(this);
// 2. Let lenValue be the result of calling the Get internal method
// of O with the argument "length".
// 3. Let len be ToUint32(lenValue).
var len = O.length >>> 0;
// 4. If IsCallable(callbackfn) is false, throw a TypeError exception.
if (typeof callbackfn !== 'function' && Object.prototype.toString.call(callbackfn) !== '[object Function]') {
throw new TypeError();
}
// 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
if (arguments.length > 1) {
T = thisArg;
}
// 6. Let k be 0.
k = 0;
// 7. Repeat, while k < len
while (k < len) {
var kValue;
// a. Let Pk be ToString(k).
// This is implicit for LHS operands of the in operator
// b. Let kPresent be the result of calling the HasProperty internal
// method of O with argument Pk.
// This step can be combined with c
// c. If kPresent is true, then
if (k in O) {
var testResult;
// i. Let kValue be the result of calling the Get internal method
// of O with argument Pk.
kValue = O[k];
// ii. Let testResult be the result of calling the Call internal method
// of callbackfn with T as the this value if T is not undefined
// else is the result of calling callbackfn
// and argument list containing kValue, k, and O.
if(T) testResult = callbackfn.call(T, kValue, k, O);
else testResult = callbackfn(kValue,k,O)
// iii. If ToBoolean(testResult) is false, return false.
if (!testResult) {
return false;
}
}
k++;
}
return true;
};
const arFalsey = [0, false, undefined, null];
const arTruthy = [33, "Hello", true, []];
console.log(arFalsey.PolyFillEvery(x=>x)); //false
console.log(arTruthy.PolyFillEvery(x=>x)); //true
console.log([true,false].PolyFillEvery(x=>x)); //false
The crucial part is if-clause in the inside of the loop:
// iii. If ToBoolean(testResult) is false, return false.
if (!testResult) {
return false;
}
As soon as one of the elements is not tested truthy false is returned; the spread (...) expression is simply used to iterate the array and instead of a traditional function an arrow function x => x is used to feed (via return) each element as is to the test in the every function.
Can someone explain to me how are array methods aware of the value that we called them on,
As part of the prototype inheritance shouldn't it exist on the Array.prototype
if we say
let animals = ['dog','cat']
animals.map( x => console.log(x))
//dog
//cat
I just don't understand how is map aware of that we passed ['dog','cat'].
I usually see that if you call a function then you need to call it like map(animals).
thank you in advance
or looking at the map polyfill where is the line that we assign it to the arguments of the array.
if (!Array.prototype.map) {
Array.prototype.map = function(callback/*, thisArg*/) {
var T, A, k;
if (this == null) {
throw new TypeError('this is null or not defined');
}
var O = Object(this);
var len = O.length >>> 0;
// 4. If IsCallable(callback) is false, throw a TypeError exception.
// See: http://es5.github.com/#x9.11
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
if (arguments.length > 1) {
T = arguments[1];
}
A = new Array(len);
// 7. Let k be 0
k = 0;
// 8. Repeat, while k < len
while (k < len) {
var kValue, mappedValue;
if (k in O) {
// i. Let kValue be the result of calling the Get internal
// method of O with argument Pk.
kValue = O[k];
mappedValue = callback.call(T, kValue, k, O);
// For best browser support, use the following:
A[k] = mappedValue;
}
// d. Increase k by 1.
k++;
}
// 9. return A
return A;
};
}
Array.prototype.mymap = function(callback) {
var myArray = this; // here is the magic
var newArray = [];
for (var i = 0; i < myArray.length; i++) {
newArray.push(callback(myArray[i]));
}
return newArray;
};
console.log([0, 1, 2, 3].mymap(a => a * 2));
Well if we looked at the definition of Map according to W3Schools:
The map() method creates a new array with the results of calling a
function for every array element.
So the function map checks every element and does something with that.
In this example you say: "Console.log every element in this array"
So it's logic that the function map is aware what is in the array.
map() is a method of the Array object. In JavaScript, we say that the animals object uses "prototypical inheritance" of the Array object. It's kind of like saying animals inherits the properties and method of the Array base class (if you come from a world of OOP).
Since map is a function that exists for all Array objects, animals is no exception.
By definition map() takes a function, and returns an Array. It then iterates through each element of the array it was called from (in this case the animals array), and applies the passed function to each element.
Usually you would use this with a return statement in order to generate a new array. For instance, you could write something like:
var upperCaseAnimals = animals.map(x => return(x.toUpperCase()))
This would create a new array, upperCaseAnimals which contains all elements of the animals array with toUpperCase() applied to them.
But you can also use it to "do something" with each element of the array. This is what's being done in the sample above. Instead of giving a return value for each element, you're just console.log()ing each element.
This doesnt really work in IE
[].forEach.call(document.querySelectorAll('.some-class-selector'), function(arg) {
callSomeFucntion(arg)
});
because forEach doesn't work on IE. if I want to do this using for loop, how would I do this?
Need to add this for run forEach in IE
<meta http-equiv="X-UA-Compatible" content="IE=9; IE=8; IE=7" />
What version of IE are you using? The Microsoft docs page for forEach lists the version that don't support it - mainly IE 6,7 and 8 or quirks mode (https://learn.microsoft.com/en-us/scripting/javascript/reference/foreach-method-array-javascript)
You can also use a polyfill if your browser won't support it MDN forEach:
forEach() was added to the ECMA-262 standard in the 5th edition; as
such it may not be present in other implementations of the standard.
You can work around this by inserting the following code at the
beginning of your scripts, allowing use of forEach() in
implementations that don't natively support it. This algorithm is
exactly the one specified in ECMA-262, 5th edition, assuming Object
and TypeError have their original values and that callback.call()
evaluates to the original value of Function.prototype.call().
// Production steps of ECMA-262, Edition 5, 15.4.4.18
// Reference: http://es5.github.io/#x15.4.4.18
if (!Array.prototype.forEach) {
Array.prototype.forEach = function(callback/*, thisArg*/) {
var T, k;
if (this == null) {
throw new TypeError('this is null or not defined');
}
// 1. Let O be the result of calling toObject() passing the
// |this| value as the argument.
var O = Object(this);
// 2. Let lenValue be the result of calling the Get() internal
// method of O with the argument "length".
// 3. Let len be toUint32(lenValue).
var len = O.length >>> 0;
// 4. If isCallable(callback) is false, throw a TypeError exception.
// See: http://es5.github.com/#x9.11
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
// 5. If thisArg was supplied, let T be thisArg; else let
// T be undefined.
if (arguments.length > 1) {
T = arguments[1];
}
// 6. Let k be 0.
k = 0;
// 7. Repeat while k < len.
while (k < len) {
var kValue;
// a. Let Pk be ToString(k).
// This is implicit for LHS operands of the in operator.
// b. Let kPresent be the result of calling the HasProperty
// internal method of O with argument Pk.
// This step can be combined with c.
// c. If kPresent is true, then
if (k in O) {
// i. Let kValue be the result of calling the Get internal
// method of O with argument Pk.
kValue = O[k];
// ii. Call the Call internal method of callback with T as
// the this value and argument list containing kValue, k, and O.
callback.call(T, kValue, k, O);
}
// d. Increase k by 1.
k++;
}
// 8. return undefined.
};
}
Working through Eloquent JavaScript and High Order Functions - section in Functional Programming.
Trying to define a reduce function and use it in a higher order function countWords();, which takes a given array and counts number of times each particular value is present and puts it in an object.
I.e. this works:
function combine(countMap, word) {
countMap[word] = ++countMap[word] || 1; // made the edit
return countMap;
}
function countWords(wordArray) {
return wordArray.reduce(combine, {});
}
var inputWords = ['Apple', 'Banana', 'Apple', 'Pear', 'Pear', 'Pear'];
countWords(inputWords); // {Apple: 2, Banana: 1, Pear: 3}
I.e. this does not:
function combine(countMap, word) {
countMap[word] = ++countMap[word] || 1;
return countMap;
}
function forEach(array, action) {
for (var i = 0; i < array.length; i++) {
action(array[i]);
}
}
function reduce(fn, base, array) {
forEach(array, function (element) {
base = fn(base, element);
});
return base;
}
function countWords(wordArray) {
return reduce(combine, {}, wordArray);
}
var inputWords = ['Apple', 'Banana', 'Apple', 'Pear', 'Pear', 'Pear'];
countWords(inputWords); // returned this - [object Object] { ... } - this is no longer an issue after fix keeping it noted for reference to the original issue.
Any help on this would be great. Thanks.
Your original reduce is actually broken, despite you saying that it works.
Here's a reduce that actually functions
var words = ["foo", "bar", "hello", "world", "foo", "bar"];
var wordIndexer = function(map, word) {
map[word] = map[word] || 0;
map[word]++;
return map;
};
var count = word.reduce(wordIndexer, {});
console.log(count);
// Object {foo: 2, bar: 2, hello: 1, world: 1}
That said, I'm not entirely sure what you're trying to do with the second half of your post. Are you just trying to write implementations for forEach and reduce so you can understand how they work?
I would write the forEach like this
var forEach = function(arr, callback) {
for (var i=0, len=arr.length; i<len; i++) {
callback(arr[i], i, arr);
}
return arr;
};
And reduce like this
var reduce = function(arr, callback, initialValue) {
var result = initialValue;
forEach(arr, function(elem, idx) {
result = callback(result, elem, idx, arr);
});
return result;
};
Test them out
var numbers = [10, 20, 30];
forEach(numbers, function(num, idx) {
console.log(idx, num);
});
// 0, 10
// 1, 20
// 2, 30
//=> [10, 20, 30]
var n = reduce(numbers, function(sum, num, idx, arr) {
return sum = sum + num;
}, 0);
console.log(n);
//=> 60
For those curious about reduce callback, I matched the native .reduce callback
I guess it depends on if you want forEach and reduce to be similar (simple) or as close as possible/reasonable to the ECMA5 spec (ignoring browser bugs), I like close as possible/reasonable.
Array.prototype.forEach ( callbackfn [ , thisArg ] )
callbackfn should be a function that accepts three arguments. forEach calls callbackfn once for each element present in the array, in ascending order. callbackfn is called only for elements of the array which actually exist; it is not called for missing elements of the array.
If a thisArg parameter is provided, it will be used as the this value for each invocation of callbackfn. If it is not provided, undefined is used instead.
callbackfn is called with three arguments: the value of the element, the index of the element, and the object being traversed.
forEach does not directly mutate the object on which it is called but the object may be mutated by the calls to callbackfn.
The range of elements processed by forEach is set before the first call to callbackfn. Elements which are appended to the array after the call to forEach begins will not be visited by callbackfn. If existing elements of the array are changed, their value as passed to callback will be the value at the time forEach visits them; elements that are deleted after the call to forEach begins and before being visited are not visited.
When the forEach method is called with one or two arguments, the following steps are taken:
Let O be the result of calling ToObject passing the this value as the argument.
Let lenValue be the result of calling the [[Get]] internal method of O with the argument "length".
Let len be ToUint32(lenValue).
If IsCallable(callbackfn) is false, throw a TypeError exception.
If thisArg was supplied, let T be thisArg; else let T be undefined.
Let k be 0.
Repeat, while k < len
Let Pk be ToString(k).
Let kPresent be the result of calling the [[HasProperty]] internal method of O with argument Pk.
If kPresent is true, then
Let kValue be the result of calling the [[Get]] internal method of O with argument Pk.
Call the [[Call]] internal method of callbackfn with T as the this value and argument list containing kValue, k, and O.
Increase k by 1.
Return undefined.
The length property of the forEach method is 1.
NOTE The forEach function is intentionally generic; it does not require that its this value be an Array object. Therefore it can be transferred to other kinds of objects for use as a method. Whether the forEach function can be applied successfully to a host object is implementation-dependent.
-
Array.prototype.reduce ( callbackfn [ , initialValue ] )
callbackfn should be a function that takes four arguments. reduce calls the callback, as a function, once for each element present in the array, in ascending order.
callbackfn is called with four arguments: the previousValue (or value from the previous call to callbackfn), the currentValue (value of the current element), the currentIndex, and the object being traversed. The first time that callback is called, the previousValue and currentValue can be one of two values. If an initialValue was provided in the call to reduce, then previousValue will be equal to initialValue and currentValue will be equal to the first value in the array. If no initialValue was provided, then previousValue will be equal to the first value in the array and currentValue will be equal to the second. It is a TypeError if the array contains no elements and initialValue is not provided.
reduce does not directly mutate the object on which it is called but the object may be mutated by the calls to callbackfn.
The range of elements processed by reduce is set before the first call to callbackfn. Elements that are appended to the array after the call to reduce begins will not be visited by callbackfn. If existing elements of the array are changed, their value as passed to callbackfn will be the value at the time reduce visits them; elements that are deleted after the call to reduce begins and before being visited are not visited.
When the reduce method is called with one or two arguments, the following steps are taken:
Let O be the result of calling ToObject passing the this value as the argument.
Let lenValue be the result of calling the [[Get]] internal method of O with the argument "length".
Let len be ToUint32(lenValue).
If IsCallable(callbackfn) is false, throw a TypeError exception.
If len is 0 and initialValue is not present, throw a TypeError exception.
Let k be 0.
If initialValue is present, then
Set accumulator to initialValue.
Else, initialValue is not present
Let kPresent be false.
Repeat, while kPresent is false and k < len
Let Pk be ToString(k).
Let kPresent be the result of calling the [[HasProperty]] internal method of O with argument Pk.
If kPresent is true, then
Let accumulator be the result of calling the [[Get]] internal method of O with argument Pk.
Increase k by 1.
If kPresent is false, throw a TypeError exception.
Repeat, while k < len
Let Pk be ToString(k).
Let kPresent be the result of calling the [[HasProperty]] internal method of O with argument Pk.
If kPresent is true, then
Let kValue be the result of calling the [[Get]] internal method of O with argument Pk.
Let accumulator be the result of calling the [[Call]] internal method of callbackfn with undefined as the this value and argument list containing accumulator, kValue, k, and O.
Increase k by 1.
Return accumulator.
The length property of the reduce method is 1.
NOTE The reduce function is intentionally generic; it does not require that its this value be an Array object. Therefore it can be transferred to other kinds of objects for use as a method. Whether the reduce function can be applied successfully to a host object is implementation-dependent.
Which for me I would write (and these are not 100% to spec, but close) and keep in my personal library.
function firstToCapital(inputString) {
return inputString.charAt(0).toUpperCase() + inputString.slice(1).toLowerCase();
}
function isClass(inputArg, className) {
return Object.prototype.toString.call(inputArg) === '[object ' + firstToCapital(className) + ']';
}
function checkObjectCoercible(inputArg) {
if (typeof inputArg === 'undefined' || inputArg === null) {
throw new TypeError('Cannot convert argument to object');
}
return inputArg;
};
function ToObject(inputArg) {
checkObjectCoercible(inputArg);
if (isClass(inputArg, 'boolean')) {
inputArg = new Boolean(inputArg);
} else if (isClass(inputArg, 'number')) {
inputArg = new Number(inputArg);
} else if (isClass(inputArg, 'string')) {
inputArg = new String(inputArg);
}
return inputArg;
}
function ToUint32(inputArg) {
return inputArg >>> 0;
}
function throwIfNotAFunction(inputArg) {
if (!isClass(inputArg, 'function')) {
throw TypeError('Argument is not a function');
}
return inputArg;
}
function forEach(array, fn, thisArg) {
var object = ToObject(array),
length,
index;
throwIfNotAFunction(fn);
length = ToUint32(object.length);
for (index = 0; index < length; index += 1) {
if (index in object) {
fn.call(thisArg, object[index], index, object);
}
}
}
function reduce(array, fn, initialValue) {
var object = ToObject(array),
accumulator,
length,
kPresent,
index;
throwIfNotAFunction(fn);
length = ToUint32(object.length);
if (!length && arguments.length === 2) {
throw new TypeError('reduce of empty array with no initial value');
}
index = 0;
if (arguments.length > 2) {
accumulator = initialValue;
} else {
kPresent = false;
while (!kPresent && index < length) {
kPresent = index in object;
if (kPresent) {
accumulator = object[index];
index += 1;
}
}
if (!kPresent) {
throw new TypeError('reduce of empty array with no initial value');
}
}
while (index < length) {
if (index in object) {
accumulator = fn.call(undefined, accumulator, object[index], index, object);
}
index += 1;
}
return accumulator;
}
function keys(object) {
if (!isClass(object, 'object') && !isClass(object, 'function')) {
throw new TypeError('Argument must be an object or function');
}
var props = [],
prop;
for (prop in object) {
if (object.hasOwnProperty(prop)) {
props.push(prop);
}
}
return props;
}
var inputWords = ['Apple', 'Banana', 'Apple', 'Pear', 'Pear', 'Pear'];
var counts = reduce(inputWords, function (previous, element) {
previous[element] = ++previous[element] || 1;
return previous;
}, {});
forEach(keys(counts), function (key) {
console.log(key, this[key]);
}, counts);
On jsFiddle
Of course this may be a little OTT for what you are doing. :)
The reason is that your ForEach implementation is wrong. you should set i = 0;
function forEach(array, action) {
for(var i = 0; i < array.length; i++) {
action(array[i]);
}
}
There seems to be something wrong. You update an object. ++countMap
function combine(countMap, word) {
countMap[word] = ++countMap || 1;
return countMap;
}
It should be
function combine(countMap, word) {
countMap[word] = ++countMap[word] || 1;
return countMap;
}
I add a jsbin here
This code runs great on Chrome, FFX, etc. It takes the content of a textarea and separates all lines in different array items (new lines are represented by empty array items). When testing it on IE, it throws an error. Code:
This is tregex's value and the call:
var tregex = /\n|([^\r\n.!?]+([.!?]+|$))/gim;
var source = $('#text').val().match(tregex).map($.trim);
The code throws this error message because of .map() (IE only)
User Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1;
Trident/4.0; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR
3.5.30729) Timestamp: Fri, 13 Jul 2012 21:52:48 UTC
Message: Object doesn't support this property or method Line: 128
Char: 6 Code: 0 URI: http://mydomain.com/src/common.js
Why? Any way I can support it on IE7+? (this was tested on IE8).
IE is a terrible browser, and as such doesn't have a built-in map function (at least not in IE7-8). Since you're trying to call map on the results of a Regular Expression match (as opposed to calling it on a jQuery results object), the only map you can use is the built-in one (that IE doesn't have).
There are many libraries that simulate map for you however, including jQuery, Underscore, and Mochikit.
Here's an example of how you could use jQuery's to do what you're trying to do:
$.map($('#text').val().match(tregex), $.trim);
I believe your code may be working in FF and Chrome by accident. Did you mean to use jQuery.map? You are using Array.map which isn't supported in IE prior to 9.
Consider the following as a replacement.
var source = $.map($('#text').val().match(tregex), $.trim);
This uses jQuery's map implementation to loop through and do the trim.
I'm fairly sure Array.map() was only added in IE9. You should loop through the array manually instead.
You can add the support just by including the shim for it:
// Production steps of ECMA-262, Edition 5, 15.4.4.19
// Reference: http://es5.github.com/#x15.4.4.19
if (!Array.prototype.map) {
Array.prototype.map = function(callback, thisArg) {
var T, A, k;
if (this == null) {
throw new TypeError(" this is null or not defined");
}
// 1. Let O be the result of calling ToObject passing the |this| value as the argument.
var O = Object(this);
// 2. Let lenValue be the result of calling the Get internal method of O with the argument "length".
// 3. Let len be ToUint32(lenValue).
var len = O.length >>> 0;
// 4. If IsCallable(callback) is false, throw a TypeError exception.
// See: http://es5.github.com/#x9.11
if ({}.toString.call(callback) != "[object Function]") {
throw new TypeError(callback + " is not a function");
}
// 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
if (thisArg) {
T = thisArg;
}
// 6. Let A be a new array created as if by the expression new Array(len) where Array is
// the standard built-in constructor with that name and len is the value of len.
A = new Array(len);
// 7. Let k be 0
k = 0;
// 8. Repeat, while k < len
while(k < len) {
var kValue, mappedValue;
// a. Let Pk be ToString(k).
// This is implicit for LHS operands of the in operator
// b. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk.
// This step can be combined with c
// c. If kPresent is true, then
if (k in O) {
// i. Let kValue be the result of calling the Get internal method of O with argument Pk.
kValue = O[ k ];
// ii. Let mappedValue be the result of calling the Call internal method of callback
// with T as the this value and argument list containing kValue, k, and O.
mappedValue = callback.call(T, kValue, k, O);
// iii. Call the DefineOwnProperty internal method of A with arguments
// Pk, Property Descriptor {Value: mappedValue, Writable: true, Enumerable: true, Configurable: true},
// and false.
// In browsers that support Object.defineProperty, use the following:
// Object.defineProperty(A, Pk, { value: mappedValue, writable: true, enumerable: true, configurable: true });
// For best browser support, use the following:
A[ k ] = mappedValue;
}
// d. Increase k by 1.
k++;
}
// 9. return A
return A;
};
}
You can also include the es5shim which adds .map and all the other missing array methods in IE7-8 plus other goods like Function#bind