Element-wise Operations In Javascript
Asked Answered
A

6

28

I'm doing some physics simulations which of course involve vectors. This has become very difficult for me because to the best of my knowledge javascript doesn't support anything like this...

#with the aid of numpy
>>> a = np.array([1,2,3])
>>> b = np.array([9,2,7])
>>> a+b
array([10,  4, 10])

I've been able to work around this limitation by defining functions that will achieve the same thing, but my formulas end up looking like this:

add(x, add( mult(v,dt), mult(mult( a(x), .5), Math.pow(dt,2))))

So my question is whether there are better ways to achieve this functionality, whether they be features of the language I am unaware of, libraries that address this issue, or more effective ways to deal with it.

Thanks for the help all.

Approach answered 21/8, 2011 at 1:54 Comment(2)
just adding each element of an array to a corresponding element in another array?Sanity
well....yeah. Ideally do it with the relative ease I've demonstrated with Numpy. My solution as you've seen is abhorrent.Approach
C
16

Check out Sylvester. I think it might be what you are looking for.

But if you wanted to implement the objects yourself, then it might be better to do a more OOP approach. JavaScript is a prototype-based language, so it different a little bit from other OOP languages, but its still pretty easy to implement your own prototypes.

Something like:

Vector = function(items) {
    this.items = items
}

Vector.prototype.add = function(other) {
    var result = []
    for(var i = 0; i < this.items; i++) {
        result.push( this.items[i] + other.items[i])
    }

    return new Vector(result);
}

Vector.prototype.subtract = function(other) { /* code to subtract */ }
Vector.prototype.multiply = function(other) { /* code to multiply */ }

And then use them like this:

var a = new Vector([1,2,3]);
var b = new Vector([5,0,1]);

var result = a.add(b)
result.items // [6,2,4]

Or if you wanted to, you could also extend the Array class with some functions with

Array.prototype.vectorAdd = function(other) { /* code to add another array as a vector */ };

And call that using

[1,2,3].vectorAdd([5,0,1])

Hopefully, that might give you a starting point to make your code a little more readable.

Just another note: Unfortunately in this case, JavaScript doesn't support operation overloading so you can't do neat stuff like a+b. You'll have to do something like a.add(b). but as long you return an appropriate object you can chain methods together. Like:

a.add(b).multiply(c).subtract(d);

ps. the presented code might be a little "off", I just typed it up off the top of my head, so treat it more like pseduocode :)

Claudication answered 21/8, 2011 at 2:34 Comment(10)
+1 Personally don't like the API, but seems like a great functional libraryArvonio
Basically having a variable var o = Vector() return 01 for an id when valueOf is called while another variable var p = Vector() would return 0100 as an id. Then you could potentially do Vector( o * p ) reverse determine the originally ids to construct a new Vector. Basically it would involve a whole lot of monkey patching... Basically overriding the valueOf operator you can actually create Objects with assigned ids that can allow you to simulate property overloading (to a semi limited extent) It would get really tricky when the division operator is used /Attar
@Attar you think you could but together a small demo of that? :D I'm not quite sure if I completely understand what you are suggesting.Approach
@32bitkid +1 Thanks for the suggestion! I'm really impressed with the functionality, and I'm trying to use it right now. But, like someone else said, I'm not to crazy about the API itself.Approach
@32bitkid Doing Array.prototype.slice.call(arguments,0) instead of items could make a slightly nicer syntax Vector(0,1,2) instead of Vector([0,1,2]). @Pete 1st Do you understand how valueOf in javascript works?Attar
@Attar Not at all. :D But I'm about to read the entry about it on MDN.Approach
@Lime, agreed on the nicer syntax. I thought about it, but I wanted to keep it as simple and readable as possible.Claudication
@32bitkid Yeah I would just use the text names with chaining and keep it simpleAttar
@Attar I prefer the Vector([0,1,2]) style syntax simply for performance reasons: accessing the arguments object in some browsers is astonishingly slow, and OP will probably be constructing a whole lot of these in a physics simulation.Nomology
I'm a total newb in JS but trying this approach, the push didn't work for some reason, and the result is a length 0 object within the implementation of the functions. so add returns undefined if we try to access [0] for instance.Xanthophyll
R
22

we can use the map function to add array elements:

function addvector(a,b){
    return a.map((e,i) => e + b[i]);
}
addvector([2,3,4],[4,7,90]) # returns [6,10,94]
Regen answered 20/3, 2017 at 14:57 Comment(3)
This is a great solution, without any third libs.Issuance
This isnt actually about a loop. Numpy uses SIMD kind of computation and the time complexity for it is far lesser than O(n).Marigraph
@OmkarKulkarni SIMD is fixed width and only speeds up by a constant factor.Nocturn
C
16

Check out Sylvester. I think it might be what you are looking for.

But if you wanted to implement the objects yourself, then it might be better to do a more OOP approach. JavaScript is a prototype-based language, so it different a little bit from other OOP languages, but its still pretty easy to implement your own prototypes.

Something like:

Vector = function(items) {
    this.items = items
}

Vector.prototype.add = function(other) {
    var result = []
    for(var i = 0; i < this.items; i++) {
        result.push( this.items[i] + other.items[i])
    }

    return new Vector(result);
}

Vector.prototype.subtract = function(other) { /* code to subtract */ }
Vector.prototype.multiply = function(other) { /* code to multiply */ }

And then use them like this:

var a = new Vector([1,2,3]);
var b = new Vector([5,0,1]);

var result = a.add(b)
result.items // [6,2,4]

Or if you wanted to, you could also extend the Array class with some functions with

Array.prototype.vectorAdd = function(other) { /* code to add another array as a vector */ };

And call that using

[1,2,3].vectorAdd([5,0,1])

Hopefully, that might give you a starting point to make your code a little more readable.

Just another note: Unfortunately in this case, JavaScript doesn't support operation overloading so you can't do neat stuff like a+b. You'll have to do something like a.add(b). but as long you return an appropriate object you can chain methods together. Like:

a.add(b).multiply(c).subtract(d);

ps. the presented code might be a little "off", I just typed it up off the top of my head, so treat it more like pseduocode :)

Claudication answered 21/8, 2011 at 2:34 Comment(10)
+1 Personally don't like the API, but seems like a great functional libraryArvonio
Basically having a variable var o = Vector() return 01 for an id when valueOf is called while another variable var p = Vector() would return 0100 as an id. Then you could potentially do Vector( o * p ) reverse determine the originally ids to construct a new Vector. Basically it would involve a whole lot of monkey patching... Basically overriding the valueOf operator you can actually create Objects with assigned ids that can allow you to simulate property overloading (to a semi limited extent) It would get really tricky when the division operator is used /Attar
@Attar you think you could but together a small demo of that? :D I'm not quite sure if I completely understand what you are suggesting.Approach
@32bitkid +1 Thanks for the suggestion! I'm really impressed with the functionality, and I'm trying to use it right now. But, like someone else said, I'm not to crazy about the API itself.Approach
@32bitkid Doing Array.prototype.slice.call(arguments,0) instead of items could make a slightly nicer syntax Vector(0,1,2) instead of Vector([0,1,2]). @Pete 1st Do you understand how valueOf in javascript works?Attar
@Attar Not at all. :D But I'm about to read the entry about it on MDN.Approach
@Lime, agreed on the nicer syntax. I thought about it, but I wanted to keep it as simple and readable as possible.Claudication
@32bitkid Yeah I would just use the text names with chaining and keep it simpleAttar
@Attar I prefer the Vector([0,1,2]) style syntax simply for performance reasons: accessing the arguments object in some browsers is astonishingly slow, and OP will probably be constructing a whole lot of these in a physics simulation.Nomology
I'm a total newb in JS but trying this approach, the push didn't work for some reason, and the result is a length 0 object within the implementation of the functions. so add returns undefined if we try to access [0] for instance.Xanthophyll
A
5

Don't know if this will help, but you can add methods to Array or Number by extending the constructor's .protoype object.

Example: http://jsfiddle.net/9JwLd/

Array.prototype.add = function( b ) {
    var a = this,
        c = [];
    if( Object.prototype.toString.call( b ) === '[object Array]' ) {
        if( a.length !== b.length ) {
            throw "Array lengths do not match.";
        } else {
            for( var i = 0; i < a.length; i++ ) {
                c[ i ] = a[ i ] + b[ i ];
            }
        }
    } else if( typeof b === 'number' ) {
        for( var i = 0; i < a.length; i++ ) {
            c[ i ] = a[ i ] + b;
        }
    }
    return c;
};
var a = [1,2,3];
var b = [9,2,7];

   // pass an Array
var c = a.add( b );  // [10,4,10]

   // pass a number
var d = a.add( 5 );  // [6,7,8]

The next version of JavaScript (ECMAScript) will likely include Array comprehensions, which may help as well. (Currently supported in SpiderMonkey.)

EXAMPLE: http://jsfiddle.net/dj6Eq/ (Test in newer versions of Firefox.)

var a = [1, 2, 3];
var b = [9, 2, 7];

var c = [a[n]+b[n] for (n in a) ];
var d = [a[n]+5 for (n in a) ];

EDIT: According to the proposal the syntax will be a little different than the current Mozilla implementation of Array comprehensions.

Abundance answered 21/8, 2011 at 2:49 Comment(2)
+1 Oh wow, I had no idea you could do that. Thanks! Edit: That second version only works in FF? I would simply do a comprehension if it was currently supported in chrome... :/Approach
@Pete: Yeah, just Firefox right now. I wouldn't be too surprised if Chrome implemented the ECMAScript syntax a before the release of the new spec. Hard to say though. Here's a video of a talk that David Herman of Mozilla gave earlier this year on the next version of ECMAScript. Interesting stuff. Also, you may want to check out CoffeeScript which supports alternate syntax for JavaScript, including comprehensions.Abundance
C
5

Using zipWith from lodash (https://lodash.com/):

_.zipWith([1, 2, 3], [9, 2, 7], _.add);
// -> [10, 4, 10]
Carree answered 11/8, 2015 at 18:6 Comment(0)
S
3

For just adding arrays in js, you can use this function

function addArrays(ar1, ar2){
    var ar3 = [];
    for(var i in ar1)
        ar3.push(ar1[i] + ar2[i]);
    return ar3;
}

and then call it like so

var array1 = [1,4,3];
var array2 = [5,3,2];
var array3 = addArrays(array1,array2);
// value of array3 is [6,7,5]
Sanity answered 21/8, 2011 at 2:47 Comment(0)
S
0

I like the answer of Varn K using map and extended it to handle an arbitrary number of arrays as input:

/**
 * @param {number[][]} arrays
 * @returns {number[]} Array as element-wise sum of the input arrays
 */
function addArrays(arrays) {
    return arrays.reduce((prev, curr) => prev.map((element, index) => element + curr[index]));
}

You might want to add checks for null and dimensions.

Synchronism answered 21/2, 2023 at 8:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.