By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
424,842 Members | 2,186 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 424,842 IT Pros & Developers. It's quick & easy.

Perlish map() function

P: n/a
Perl's map() function is a powerful shortcut to accomplish many "loop over
an array and do X" operations.
Below is a quick hack to simulate similar functionality.
I've used it a few times and find it to be quite handy.
Any thoughts?

<script type="text/javascript">
function map(func) {
var i,j,o;
var results = [];
if (typeof(func)=="string") {
func = new Function('$_',func);
}
for (i=1; i<arguments.length; i++) {
o = arguments[i];
if (typeof(o)=="object" && o.length) {
for (j=0; j<o.length; j++) {
results[results.length] = func(o[j]);
}
}
else if (typeof(o)=="object") {
for (j in o) {
results[results.length] = func(o[j]);
}
}
else {
results[results.length] = func(o);
}
}
return results;
}
var o = { 'a':'a1', 'b':'b1', 'c':'c1' };
var a = [ 'a','b','c' ];

map( "alert($_)", o , a, "test" );
map( function(o){ alert("Value:"+o); } , o , a, "test" );
map( "alert($_);", map( "return $_.toUpperCase();" , o, a, "test") );
</script>

PS: The use of $_ as an identifier is carried over from Perl, in case you
aren't familiar with it.

--
Matt Kruse
http://www.JavascriptToolbox.com
http://www.AjaxToolbox.com
Feb 20 '06 #1
Share this Question
Share on Google+
17 Replies


P: n/a
Matt Kruse wrote:
Perl's map() function is a powerful shortcut to accomplish many "loop over
an array and do X" operations.
Below is a quick hack to simulate similar functionality.
I've used it a few times and find it to be quite handy.
Any thoughts?
Array.prototype.map() exists since JavaScript 1.6 (Gecko 1.8), you are
"too late" ;-)

Seriously, the application of your method seems more elegant for arbitrary
values than that of this built-in method (and your method is required in
JScript and JavaScript < 1.6 of course). I have included the (supposedly)
equivalent JavaScript 1.6 code below for completeness.
<script type="text/javascript">
function map(func) {
var i,j,o;
var results = [];
if (typeof(func)=="string") {
`typeof' is an operator, not a function:

if (typeof func == "string") {
func = new Function('$_',func);
}
for (i=1; i<arguments.length; i++) {
Since `i' is used only for this loop, I would declare it here:

for (var i = 1, len = arguments.length; i < len; i++) {
o = arguments[i];
if (typeof(o)=="object" && o.length) {
if (typeof o == "object" && o.length) {

Maybe one should test for o[0], too.
for (j=0; j<o.length; j++) {
var len;
for (j = 0, len = o.length; j < len; j++) {
results[results.length] = func(o[j]);
// since JavaScript 1.2, JScript 5.5
results.push(func(o[j]));
}
}
else if (typeof(o)=="object") {
for (j in o) {
results[results.length] = func(o[j]);
See above.
}
}
else {
results[results.length] = func(o);
See above.
}
}
return results;
}
var o = { 'a':'a1', 'b':'b1', 'c':'c1' };
var a = [ 'a','b','c' ];

map( "alert($_)", o , a, "test" );
That would be in JavaScript 1.6:

Object.prototype.map = function(f)
{
for (var i in this)
{
if (i != 'map')
{
f(this[i]);
}
}
};

// do not inherit this from Object.prototype
// (else we handle each character of the string)
String.prototype.map = function(f)
{
f(this);
};

[o, a, "test"].map(
function(x)
{
x.map(function(y) { alert(y); });
});
map( function(o){ alert("Value:"+o); } , o , a, "test" );
[o, a, "test"].map(
function(x)
{
x.map(function(y) { alert("Value:" + y); });
});
map( "alert($_);", map( "return $_.toUpperCase();" , o, a, "test") );
This one-liner becomes quite ugly with Array.prototype.map() only:

this.object_prototype_map = Object.prototype.map;
Object.prototype.map = function(f)
{
for (var i in this)
{
if (i != 'map')
{
this[i] = f(this[i]);
}
}

return this;
};

this.string_prototype_map = String.prototype.map;
String.prototype.map = function(f)
{
return f(this);
};

var a2 = [o, a, "test"].map(
function(x)
{
return x.map(
function(y)
{
return y.toUpperCase();
});
});

Object.prototype.map = this.object_prototype_map;
delete this.object_prototype_map;
String.prototype.map = this.string_prototype_map;
delete this.string_prototype_map;

a2.map(
function(x)
{
return x.map(
function(y)
{
alert(x);
});
});
[...]

Regards,
PointedEars
Feb 20 '06 #2

P: n/a
Thomas 'PointedEars' Lahn wrote:
Array.prototype.map() exists since JavaScript 1.6 (Gecko 1.8), you are
"too late" ;-)
I'm aware of this, but mine has more functionality, and isn't limited to
Arrays. (And can actually be used in a WWW context!)
Particularly, for my use, I often want to act on all items in an Object.
Also, I like being able to specify a quick and simple expression rather than
a full anonymous function.
if (typeof(func)=="string") {

`typeof' is an operator, not a function:


I know. But I just think typeof(obj) is clearer, so I always write it that
way.
if (typeof o == "object" && o.length) {
Maybe one should test for o[0], too.
I've actually revised it to this:
if ((typeof(o)=="object" && typeof(o.length)=="number" && (!Array || (o
instanceof Array)))) {
// since JavaScript 1.2, JScript 5.5
results.push(func(o[j]));
Old habits die hard. Despite 1.2 being widespread enough to be assumed, I
tend to avoid using 1.2 array methods, since it doesn't hurt anything to do
it the old way. I suppose I should update my thinking.

mine:
map( function(o){ alert("Value:"+o); } , o , a, "test" );


your example: [o, a, "test"].map(
function(x)
{
x.map(function(y) { alert("Value:" + y); });
});


I definitely think mine is easier to read! While the map() method in 1.6 is
no doubt useful, in general I think I would always prefer to use a method
like I've written. Which, btw, I've changed to:

function map(func) {
var i,j,o;
var results = [];
if (typeof(func)=="string") {
func = new Function('$_',func);
}
for (i=1; i<arguments.length; i++) {
o = arguments[i];
if ((typeof(o)=="object" && typeof(o.length)=="number" && (!Array || (o
instanceof Array)))) {
for (j=0; j<o.length; j++) {
results.push(func(o[j]));
}
}
else if (typeof(o)=="object" && (!Object || (o instanceof Object))) {
for (j in o) {
results.push(func(o[j]));
}
}
else {
results.push(func(o));
}
}
return results;
}

I found in testing that if I wanted to act on a group of Nodes, for example,
it would instead try to pull out its properties and act on each of those.
Thus the "instanceof Object" check.

Thanks for the constructive feedback.

--
Matt Kruse
http://www.JavascriptToolbox.com
http://www.AjaxToolbox.com
Feb 20 '06 #3

P: n/a
Matt Kruse wrote:
Thomas 'PointedEars' Lahn wrote:
Array.prototype.map() exists since JavaScript 1.6 (Gecko 1.8), you are
"too late" ;-)
I'm aware of this, but mine has more functionality, and isn't limited to
Arrays. (And can actually be used in a WWW context!)
Particularly, for my use, I often want to act on all items in an Object.
Also, I like being able to specify a quick and simple expression rather
than a full anonymous function.


See below.
if (typeof(func)=="string") {

`typeof' is an operator, not a function:


I know. But I just think typeof(obj) is clearer, so I always write it that
way.


I do not find it more clear because it makes it harder to see the method
calls. But maybe that is just me.
if (typeof o == "object" && o.length) {
Maybe one should test for o[0], too.


I've actually revised it to this:
if ((typeof(o)=="object" && typeof(o.length)=="number" && (!Array || (o
instanceof Array)))) {


`!Array' equals `false' always.
// since JavaScript 1.2, JScript 5.5
results.push(func(o[j]));


Old habits die hard. Despite 1.2 being widespread enough to be assumed, I
tend to avoid using 1.2 array methods, since it doesn't hurt anything to
do it the old way. I suppose I should update my thinking.


Array.prototype.push() is supported _since_ JavaScript 1.2 (NN 4.0),
i.e. it is supported by all later versions. That it was introduced
in that particular JavaScript version matters only insofar that it
is not supported before the corresponding NN version.

Whether you should update your thinking here or not mostly depends
on whether you want to support IE 5.0 and older or not. Iff the
MSDN Library is correct about the JScript version here.
mine:
map( function(o){ alert("Value:"+o); } , o , a, "test" );

your example:
[o, a, "test"].map(
function(x)
{
x.map(function(y) { alert("Value:" + y); });
});


I definitely think mine is easier to read! While the map() method in 1.6
is no doubt useful, in general I think I would always prefer to use a
method like I've written.


I did say quite the same, did I not?
[...]
Thanks for the constructive feedback.


You are welcome.
PointedEars
Feb 20 '06 #4

P: n/a
Matt Kruse wrote:
Thomas 'PointedEars' Lahn wrote:
Array.prototype.map() exists since JavaScript 1.6 (Gecko 1.8), you are
"too late" ;-)
I'm aware of this, but mine has more functionality, and isn't limited to
Arrays. (And can actually be used in a WWW context!)
Particularly, for my use, I often want to act on all items in an Object.


And you're most definitely not alone. Worse yet, in Javascript if
you've dared to augment the Object prototype, you'll in all likelihood
often end up with:

for (j in Obj) {
if ( Obj.hasOwnproperty(j)) {

}
}

which can become irksome, tiresome and untidy. For me, that lead to
creation of an Object.prototype.Do function that, in parameterization
and in body, is almost identical to your Perl 'map' function emulation.
That object-oriented prototypical approach seems to me to more in
keeping with the Javascript programming paradigm.

However, if your goal is to to provide an emulation of Perl
capabilities within Javascript, that's a bit of a different matter, and
your choice. But it's really not something I personally would find
myself particularly keen on (even speaking as one who used to be
somewhat attached to Perl as a scripting language). I'd much rather see
extended capabilities included under the model provided by the
language, as opposed to creating some sort of graft that doesn't
necessarily fit well. It sounds to me like a perfectly good way to end
up with a camel rather than a horse. ;-)
Also, I like being able to specify a quick and simple expression rather than
a full anonymous function.
if (typeof(func)=="string") {

`typeof' is an operator, not a function:


I know. But I just think typeof(obj) is clearer, so I always write it that
way.


Somewhat curiously, though, you don't find a similar need to be
"clearer" when using 'instanceof' ? ;-)

../rh

Feb 21 '06 #5

P: n/a
ro********@gmail.com wrote:
For me, that lead to
creation of an Object.prototype.Do function that, in parameterization
and in body, is almost identical to your Perl 'map' function
emulation. That object-oriented prototypical approach seems to me to
more in keeping with the Javascript programming paradigm.


However, you then need to extend the Array prototype, String prototype, etc.
I prefer map to look like a built-in command. Personal preference, I
suppose.
I know. But I just think typeof(obj) is clearer, so I always write
it that way.

Somewhat curiously, though, you don't find a similar need to be
"clearer" when using 'instanceof' ? ;-)


No, because this:

(typeof obj == "string")

isn't as obvious about order of operations as this:

(typeof(obj)=="string")
or
(obj instanceof Object)

I also always write:

if (condition) {
statement;
}

over:

if (condition)
statement;

because I think clarity is more important than brevity.

--
Matt Kruse
http://www.JavascriptToolbox.com
http://www.AjaxToolbox.com
Feb 21 '06 #6

P: n/a
Thomas 'PointedEars' Lahn wrote:
`!Array' equals `false' always.


Even prior to js1.2? I don't remember exactly, but I recall writing the
syntax of

if (!Array && (obj instanceof Array))

quite a long time ago, to protect against browsers which didn't have Array
defined.
I may very well be in error, though.

--
Matt Kruse
http://www.JavascriptToolbox.com
http://www.AjaxToolbox.com
Feb 21 '06 #7

P: n/a
Matt Kruse wrote:
ro********@gmail.com wrote:
For me, that lead to
creation of an Object.prototype.Do function that, in parameterization
and in body, is almost identical to your Perl 'map' function
emulation. That object-oriented prototypical approach seems to me to
more in keeping with the Javascript programming paradigm.
However, you then need to extend the Array prototype, String prototype, etc.


That, of course, is not the case. But even if so, what is your point --
that you shouldn't prototype in a prototypal language?
I prefer map to look like a built-in command. Personal preference, I
suppose.


I suppose. Somewhat unfortunate, in that it doesn't appear that you've
really considered other options that could possibly provide better
facility and be delivered with improved integration within the
language.
I know. But I just think typeof(obj) is clearer, so I always write
it that way.

Somewhat curiously, though, you don't find a similar need to be
"clearer" when using 'instanceof' ? ;-)


No, because this:

(typeof obj == "string")

isn't as obvious about order of operations as this:

(typeof(obj)=="string")
or
(obj instanceof Object)

I also always write:

if (condition) {
statement;
}

over:

if (condition)
statement;

because I think clarity is more important than brevity.


That latter is commendable, whereas I can't say the same for the
concious act of attempting to couch a prefix operator as a function in
the name of clarity. If indication or specification of order is
necessary in an expression, that's normally done by parenthetical
grouping, but not the one you use.

You did ask for comment, by the way. ;-)

... Ron

Feb 21 '06 #8

P: n/a
VK

ro********@gmail.com wrote:
... is commendable, whereas I can't say the same for the
concious act of attempting to couch a prefix operator as a function in
the name of clarity. If indication or specification of order is
necessary in an expression, that's normally done by parenthetical
grouping, but not the one you use.


JavaScript 1.3 Language Reference, Netscape Corp. 1999

The typeof operator is used in either of the following ways:

1. typeof operand
2. typeof(operand)

The parentheses are optional.
The void operator is used in either of the following ways:

1. void(expression)
2. void expression
The parentheses surrounding the expression are optional, but it is good
style to use them.

So it *was* good style for typeof either. It even was specially
mentioned and accounted in ECMAScript specs. Again if something *was*
good style in 1998-1999 doesn't have to be good style in 2006. Fashions
for code "layout" change like dress fashions :-)
I personally still use typeof(obj) unless posting in clj : only to not
get kicks at least fot this one :-)

Still as this type of coding has a very long tradition and as it is
clearly described in ECMAScript I would move this question out of the
"code quality" category. It's merely the same issue as the evelasting
discussion where to place brakets:

function f() {
}

or

function f()
{
}

Feb 21 '06 #9

P: n/a
ro********@gmail.com wrote:
However, you then need to extend the Array prototype, String
prototype, etc. That, of course, is not the case.


Well, then, you'll internally examine and switch logic depending on the
actual object.
In general, I think that extending the Object prototype is not the best
approach because it gets applied so widely.
But even if so, what is your point
-- that you shouldn't prototype in a prototypal language?
No, just that in this case it doesn't seem to make anything easier.
What if, for example, I want to call map() on 5 static values, like

map( "alert($_);" , 'a','b','c','d','e' );

Doing this by extending Object() would be a pain and counter-intuitive, IMO.
I suppose. Somewhat unfortunate, in that it doesn't appear that
you've really considered other options that could possibly provide
better facility and be delivered with improved integration within the
language.


I've considered other options - in fact, Pointy Boy posted an example of how
to implement it with prototypes. The end result was messy, IMO.

--
Matt Kruse
http://www.JavascriptToolbox.com
http://www.AjaxToolbox.com
Feb 21 '06 #10

P: n/a
VK wrote:
ro********@gmail.com wrote:
... is commendable, whereas I can't say the same for the
concious act of attempting to couch a prefix operator as a function in
the name of clarity. If indication or specification of order is
necessary in an expression, that's normally done by parenthetical
grouping, but not the one you use.


That was probably supposed to be "conscious" above. :-)
JavaScript 1.3 Language Reference, Netscape Corp. 1999

The typeof operator is used in either of the following ways:

1. typeof operand
2. typeof(operand)

The parentheses are optional.


My recollection is that the cited reference was very good, but not
perfect (including full undestanding by the authors).

[..]
So it *was* good style for typeof either. It even was specially
mentioned and accounted in ECMAScript specs.


Here's one mention from 11.1.6:

"NOTE
This algorithm does not apply GetValue to Result(1). The principal
motivation for this is so that operators such as delete and typeof may
be applied to parenthesised expressions."

It seems pretty clear to me that the pupose of allowing parentheses is
not so you can make 'typeof' appear to be a function.

../rh

Feb 21 '06 #11

P: n/a
Matt Kruse wrote:
ro********@gmail.com wrote: [..] In general, I think that extending the Object prototype is not the best
approach because it gets applied so widely.


I agree that universal augmentation can introduce its own frustrations.
But it doesn't necessarily mean that you write it off. In general,
there are tradeoffs to be weighed.
But even if so, what is your point
-- that you shouldn't prototype in a prototypal language?


No, just that in this case it doesn't seem to make anything easier.
What if, for example, I want to call map() on 5 static values, like

map( "alert($_);" , 'a','b','c','d','e' );


Wouldn't that then be:

['a','b','c','d','e'].map("alert($_)");

?

../rh

Feb 21 '06 #12

P: n/a
Matt Kruse wrote:
Thomas 'PointedEars' Lahn wrote:
`!Array' equals `false' always.
Even prior to js1.2?


After further investigation (see below), I see that my statement is
incorrect, but to answer your question properly: not always. According to
the reference material, the Array constructor was introduced in JavaScript
1.1, so it would evaluate to `false' in that version, too. And in
J(ava)Script 1.0, where `Array' is not defined, it would most certainly
not evaluate to anything (not even `true'), instead it would result in
an error.

However, in J(ava)Script 1.0 the (Syntax)Error would occur before because
`typeof' was not introduced before JavaScript 1.1, JScript 2.0. But either
aspect strikes me as being completely academical nowadays.
I don't remember exactly, but I recall writing the syntax of

if (!Array && (obj instanceof Array))

quite a long time ago, to protect against browsers which didn't have
Array defined. I may very well be in error, though.


In fact, the above does not protect against anything because if there was
no Array constructor, the base object of the reference is `null', and
evaluation would result in a ReferenceError (exception).

This was stated on similar occasions (with different identifiers) several
times in discussions the last days. Testing with any not defined
identifier shows that it is true.

However, to have it not only stated but also explained once (and please
CMIIW), here it is in detail for those who are interested in the language's
inner workings (including curious me who found that investigation quite
interesting). Everybody else please just ignore the following section :)
__________________________________________________ _________________________

,-[ECMAScript 3 Final]
|
| 11.4.9 Logical NOT Operator ( ! )
|
| The production UnaryExpression : ! UnaryExpression is evaluated as
| follows:

(First let us [the script engine] make sure that this production can be
used and there is no syntax error. The productions for `Array' are:

Program --> SourceElements --> SourceElement --> Statement
--> ExpressionStatement
--> [lookahead !elementOf {{, function}] Expression --> Expression
--> AssignmentExpression --> ConditionalExpression
--> LogicalORExpression --> LogicalANDExpression
--> BitwiseORExpression --> BitwiseXORExpression --> BitwiseANDExpression
--> EqualityExpression --> RelationalExpression --> ShiftExpression
--> AdditiveExpression --> MultiplicativeExpression
--> UnaryExpression --> PostfixExpression --> LeftHandSideExpression
^^^^^^^^^^^^^^^
--> NewExpression --> MemberExpression --> PrimaryExpression
--> Identifier
^^^^^^^^^^
(--> IdentifierName /but not/ ReservedWord --> IdentifierName
<--> {IdentifierStart, IdentifierName IdentifierPart}
<--> IdentifierPart --> {Unicode*}.)

Obviously `Array' is also a UnaryExpression. Therefore, we [the script
engine] can continue with evaluation.)

| 1. Evaluate UnaryExpression.
| 2. Call GetValue(Result(1)).
| 3. Call ToBoolean(Result(2)).
| 4. If Result(3) is true, return false.
| 5. Return true.

Executing step 1, following the productions of the grammar for `Array'
above, we see that an Identifier token has to be evaluated:

| 11.1.2 Identifier Reference
|
| An Identifier is evaluated using the scoping rules stated in section
| 10.1.4. The result of evaluating an Identifier [is] always a value of
| type Reference.

| 10.1.4 Scope Chain and Identifier Resolution
| [...]
| During execution, the syntactic production PrimaryExpression : Identifier
| is evaluated using the following algorithm:
|
| 1. Get the next object in the scope chain. If there isn't one, go to
| step 5.
| 2. Call the [[HasProperty]] method of Result(1), passing the Identifier
| as the property.
| 3. If Result(2) is true, return a value of type Reference whose base
| object is Result(1) and whose property name is the Identifier.
| 4. Go to step 1.
| 5. Return a value of type Reference whose base object is null and whose
| property name is the Identifier.
|
| The result of evaluating an identifier is always a value of type
| Reference with its member name component equal to
| the identifier string.

It should be obvious that if not even the Global Object (the last object in
any scope chain, see section 10.2) has an `Array' property, i.e. there is
no `Array' constructor, `null' is returned as base object of the `Array'
reference. (For brevity, I will write `Reference(X, Y)' for a value of
type Reference with base object component X, and member name component Y.
So the Reference value here is Reference(null, Array).)

Let us get back to the evaluation of the Logical NOT operator:

| 1. Evaluate UnaryExpression.

Because of the above, Result(1) := Reference(null, Array)

| 2. Call GetValue(Result(1)).

Result(2) := GetValue(Reference(null, Array))

| 8.7.1 GetValue (V)
|
| 1. If Type(V) is not Reference, return V.

It was established that V is of type Reference, so nothing is done here.

| 2. Call GetBase(V).

| GetBase(V). Returns the base object component of the reference V.

It was established that the base object component of V is `null':

Result(2) := null

| 3. If Result(2) is null, throw a ReferenceError exception.

Since the condition applies, the named exception is thrown. q.e.d.

| 5.2 Algorithm Conventions
| [...]
| If an algorithm is defined to "throw an exception", execution of the
| algorithm is terminated and no result is returned.
| The calling algorithms are also terminated, until an algorithm step is
| reached that explicitly deals with the exception, using terminology such
| as "If an exception was thrown...". Once such an algorithm step has been
| encountered the exception is no longer considered to have occurred.

BTW: There is a truly marvelous proof that the `!' operator does not
matter regarding this, which this posting is too short to contain ;-)
__________________________________________________ _________________________

Back to your code. Your expression was:
if ((typeof(o)=="object" && typeof(o.length)=="number" && (!Array || (o
instanceof Array)))) {


Let us assume that the first two subexpressions were evaluated to `true'.
This forces the evaluation of the third subexpression:

(!Array || (o instanceof Array))

If the Array constructor exists (JavaScript/JScript > 1.0), the first
operand evaluates to `false', which forces the evaluation of the second
operand. However, `instanceof' is an operator specified in ECMAScript
Edition 3, not introduced before JavaScript 1.4. A SyntaxError exception
would be thrown in JavaScript 1.1 to 1.3.

If the Array constructor does not exist (JavaScript 1.0, JScript < 3.0),
and we assume that for some reason (which leaves only being JScript 2.0,
see above) the implementation supports the previous `typeof' operator, a
ReferenceError exception is thrown here, as explained in detail above.

Therefore, I would drop this subexpression completely and look for something
more generic regarding objects with numerically iterable properties. I do
not think that this can be done without giving up at least partially
backwards compatibility to JavaScript versions prior to 1.5, and JScript
versions prior to 5.5, though.

I see the following solutions to this dilemma:

A) Forget about backwards compatibility and use o.hasOwnProperty()
(JavaScript 1.5+) to determine if the object with a `length' property
has a property with numerical name that corresponds to the value of
the former. That is, if o.length == 2, test if o[1] exists; if yes,
assume that the object is numerically iterable.

B) Handle only Array objects, test for them with o.constructor == Array
(JavaScript/JScript > 1.0).

C) Handle all objects with a `length' property except String objects,
test with o.constructor == String (JavaScript/JScript > 1.0).

D) Handle all objects with a `length' property of type `number'
(JavaScript 1.1+). In JavaScript, each character of the string
value of String objects would be handled individually (which may be
inefficient); a workaround would have to be found for JScript, where
String objects have no properties with numerical names that
correspond to the characters of the string value.

Therefore, my untested suggestion, which should work in JavaScript 1.1+ /
JScript 2.0+, where collections are not supported before JavaScript 1.5 /
JScript 5.5:

var bHasOwnProperty;
if (typeof o == "object"
&& o.constructor != String
&& typeof o.length == "number"
&& (((bHasOwnProperty = (typeof o.hasOwnProperty == "function"))
&& o.hasOwnProperty(o.length - 1))
|| (!bHasOwnProperty && o.constructor == Array)))
{
for (var j = 0, len = o.length; j < len; j++)
{
// ... o[j] ...
}
}
PointedEars
Feb 22 '06 #13

P: n/a
ro********@gmail.com wrote:
Matt Kruse wrote:
ro********@gmail.com wrote:

[..]
In general, I think that extending the Object prototype is not the best
approach because it gets applied so widely.


I agree that universal augmentation can introduce its own frustrations.
But it doesn't necessarily mean that you write it off. In general,
there are tradeoffs to be weighed.
> But even if so, what is your point
> -- that you shouldn't prototype in a prototypal language?


No, just that in this case it doesn't seem to make anything easier.
What if, for example, I want to call map() on 5 static values, like

map( "alert($_);" , 'a','b','c','d','e' );


Wouldn't that then be:

['a','b','c','d','e'].map("alert($_)");

?


If you are talking about JavaScript 1.6: No, it would be

['a','b','c','d','e'].map(function(x) { alert(x); });

The complexity is introduced by variant values with properties, requiring

['a', {b: 42}, ['c'], /d/, new Error("e")].map(
function(x)
{
x.map(
function(y)
{
alert(y);
});
});

That would require every value to have a map() method. However, I find the
idea of the generic approach of only augmenting the Object prototype object
instead of augmenting all prototype objects of all Core objects (except
Array, in JavaScript 1.6) with map() quite nice. Perhaps something like
this:

Object.prototype.map = function(callback, thisObject, bSetValue)
{
if (typeof thisObject != "object"
&& typeof thisObject != "function")
{
bSetValue = thisObject;
thisObject = this;
}

if (thisObject.constructor != String)
{
for (var p in thisObject)
{
if (p != 'map')
{
if (bSetValue)
{
thisObject[p] = callback(thisObject[p]);
}
else
{
callback(thisObject[p]);
}
}
}
}
else
{
if (bSetValue)
{
return callback(thisObject);
}
else
{
callback(thisObject);
}
}
}
PointedEars
Feb 22 '06 #14

P: n/a
Thomas 'PointedEars' Lahn wrote:
ro********@gmail.com wrote:
Matt Kruse wrote:
ro********@gmail.com wrote: [..] No, just that in this case it doesn't seem to make anything easier.
What if, for example, I want to call map() on 5 static values, like

map( "alert($_);" , 'a','b','c','d','e' );

Wouldn't that then be:

['a','b','c','d','e'].map("alert($_)");

?


If you are talking about JavaScript 1.6: No, it would be

['a','b','c','d','e'].map(function(x) { alert(x); });


Well, I wasn't ;-). Matt allowed the option of a "string" or a
"function" type as the first argument to 'map', and I chose to remain
consistent with his example. The choice would generally be made based
on process complexity and/or the requirement to maintain execution
context during the mapping process.

The point mostly was that there doesn't seem to be a lot to be gained
by passing a list as arguments to the 'map' function, having those
internally converted to an Array-like 'arguments' object, and then
processing the list from there. Especially since you would usually
already have the list resident in an object.
The complexity is introduced by variant values with properties, requiring

['a', {b: 42}, ['c'], /d/, new Error("e")].map(
function(x)
{
x.map(
function(y)
{
alert(y);
});
});

That would require every value to have a map() method.
I'm not sure you're stating that exactly as intended, but you could
recurse when an object is encountered in the list (although I haven't
really thought about whether that would be consistent with expected
behavior of 'map').
However, I find the
idea of the generic approach of only augmenting the Object prototype object
instead of augmenting all prototype objects of all Core objects (except
Array, in JavaScript 1.6) with map() quite nice. Perhaps something like
this:

Object.prototype.map = function(callback, thisObject, bSetValue)
{
if (typeof thisObject != "object"
&& typeof thisObject != "function")
{
bSetValue = thisObject;
thisObject = this;
}

if (thisObject.constructor != String)
{
for (var p in thisObject)
{
if (p != 'map')
{
if (bSetValue)
{
thisObject[p] = callback(thisObject[p]);
}
else
{
callback(thisObject[p]);
}
}
}
}
else
{
if (bSetValue)
{
return callback(thisObject);
}
else
{
callback(thisObject);
}
}
}


Is there a reason you've chosen not to use 'apply' in the callback's
(other than lack of inherent availability in some browsers)?

../rh

Feb 22 '06 #15

P: n/a
Matt Kruse wrote:
ro********@gmail.com wrote:
I suppose. Somewhat unfortunate, in that it doesn't appear that
you've really considered other options that could possibly provide
better facility and be delivered with improved integration within the
language.
I've considered other options - in fact, Pointy Boy posted an example of

^^^^^^^^^^ how to implement it with prototypes. The end result was messy, IMO.


So this is the extent to what you *really* appreciate my thoughts "behind
my back".
Score adjusted

F'up2 PointedEars
Feb 23 '06 #16

P: n/a
Thomas 'PointedEars' Lahn wrote:
I've considered other options - in fact, Pointy Boy posted an
example of ^^^^^^^^^^
how to implement it with prototypes. The end result was messy, IMO.

So this is the extent to what you *really* appreciate my thoughts
"behind my back".


Eh, lighten up. It's just an attempt at humor. Humor sometimes doesn't
translate well or is misunderstood in print, unfortunately.

I appreciated your in-depth description of why !Array is flawed. I read it
twice, in fact. This thread has certainly been helpful to me, and I would
love to see more threads like it in this group.
Score adjusted


I still have no idea what this means.

--
Matt Kruse
http://www.JavascriptToolbox.com
http://www.AjaxToolbox.com
Feb 23 '06 #17

P: n/a
Matt Kruse wrote:
Thomas 'PointedEars' Lahn wrote:
I've considered other options - in fact, Pointy Boy posted an ^^^^^^^^^^
example of how to implement it with prototypes. The end result
was messy, IMO.

So this is the extent to what you *really* appreciate my thoughts
"behind my back".


Eh, lighten up. It's just an attempt at humor. Humor sometimes doesn't
translate well or is misunderstood in print, unfortunately.


ACK. This is what smileys are for:

<URL:http://en.wikipedia.org/wiki/Smiley>
I appreciated your in-depth description of why !Array is flawed. I read it
twice, in fact. This thread has certainly been helpful to me, and I would
love to see more threads like it in this group.


ACK :)
Score adjusted


I still have no idea what this means.


<URL:http://en.wikipedia.org/wiki/Scorefile>
HTH

PointedEars
Feb 23 '06 #18

This discussion thread is closed

Replies have been disabled for this discussion.