Ruby 'Each do' JS equivalent?

Discussion in 'Learning Javascript' started by Milena, Jan 17, 2016.

  1. Milena

    Milena The woman of many questions Veteran

    Messages:
    1,282
    Likes Received:
    106
    Location:
    Ireland
    First Language:
    Irish
    Primarily Uses:
    N/A
    Just wondering what's the equivalent of the each do function from Ruby to JS?

    For example:

    $data_actors.each do | actor | #do stuff here endThanks in advance.
     
    #1
  2. Iavra

    Iavra Veteran Veteran

    Messages:
    1,797
    Likes Received:
    855
    First Language:
    German
  3. Milena

    Milena The woman of many questions Veteran

    Messages:
    1,282
    Likes Received:
    106
    Location:
    Ireland
    First Language:
    Irish
    Primarily Uses:
    N/A
    Thanks. It is working now, but I wonder why it says that the actor is undefined but when I log it, it says its an Actor object. Figures.
     
    #3
  4. DoubleX

    DoubleX Just a nameless weakling Veteran

    Messages:
    1,452
    Likes Received:
    541
    First Language:
    Chinese
    Primarily Uses:
    N/A
    If your Ruby equivalent has return inside the each do:

    $data_actors.each do | actor | #do stuff here return if cond #do stuff here endThen Array.prototype.forEach won't work, as return inside forEach is equivalent to continue in a for loop. For instance, the below 2 codes are equivalent of each other:

    $dataActors.forEach(function(actor) { // do stuff here if (cond) { return; } // do stuff here});
    Code:
    for (var index = 0, length = $dataActors.length; index < length; index++) {    // do stuff here    if (cond) { continue; }    // do stuff here}
    The below is the equivalent of the above Ruby code:

    for (var index = 0, length = $dataActors.length; index < length; index++) { // do stuff here if (cond) { return; } // do stuff here}
    It depends on how your code's actually written :)
     
    #4
  5. kentaromiura

    kentaromiura Veteran Veteran

    Messages:
    46
    Likes Received:
    26
    Location:
    London
    First Language:
    English


    Just to add that a functional equivalent of this code can be achieved using `Array#some` in this way:

    Code:
    $dataActors.some(function(actor){
      if(cond) {return true}
    })
     
    Last edited by a moderator: Jan 23, 2016
    #5
    DoubleX likes this.
  6. DoubleX

    DoubleX Just a nameless weakling Veteran

    Messages:
    1,452
    Likes Received:
    541
    First Language:
    Chinese
    Primarily Uses:
    N/A
    A bit off topic: Is there any simple and small functional equivalent of the below imperative code?


    var data = this.patb_note_data();
    for (var index = 0, length = data.length; index < length; index++) {
    for (var d = data[index], i = 0, l = d.length; i < l; i++) {
    if (d.meta[note]) { return d.meta[note]; }
    }
    }
    return null;


    I've tried to write a functional equivalent but it ended up to be much more complicated and convoluted than this imperative one lol
     
    Last edited by a moderator: Jan 23, 2016
    #6
  7. Iavra

    Iavra Veteran Veteran

    Messages:
    1,797
    Likes Received:
    855
    First Language:
    German
    Honestly, i wouldn't bother. Native for-loops are always faster than Array iterators and i prefer performance over "elegant" code.


    There is an exception, though: If you want to remove elements from an array while iterating over it, you would need to iterate in reverse or also modify the index, which might not be feasible. Array.forEach creates a shallow copy from the array it iterates over and as such allows modification during the loop.
     
    Last edited by a moderator: Jan 23, 2016
    #7
    DoubleX likes this.
  8. kentaromiura

    kentaromiura Veteran Veteran

    Messages:
    46
    Likes Received:
    26
    Location:
    London
    First Language:
    English



    In EcmaScript 2015 there's a couple of new addition to the Array.prototype:


    Array#findIndex and Array#find


    the latter is what you're looking for in your inner logic, now your logic is more complicated that finding an element if an expression returns true,


    as you've nested arrays so you'll need to store the lastIndex visited in the inner loop to know the index you'll need later, I think this is an equivalent:


    var lastIndex, found = this.patb_note_data().find(function(data){
    return data.find(function(d, index){
    lastIndex = index
    return d.meta[note]
    })
    })
    return found? found[lastIndex].meta[note]: null


    to avoid reassigning lastIndex every step in the loop you can use some short circuit logic and do:


    var lastIndex, found = this.patb_note_data().find(function(data){
    return data.find(function(d, index){
    return d.meta[note] && ~(lastIndex = index)
    })
    })

    return found? found[lastIndex].meta[note]: null


    I used the bitwise not (~) operator here, since its value is falsy only if applied to the value -1, since it's impossible to have index -1 as an index of an array, the ~(lastIndex = index) always equates to a truthy value, also thanks to shortcircuiting the left part of the && expression gets evaluated only when the left parts is truthy


    It's also possible to use Array#some and directly store the value you're looking for using the same trick as above, this is possibly less readable:
     


    var found = null;
    this.patb_note_data().some(function(data){
    return data.some(function(d, index){
    return d.meta[note] && ~(found = d.meta[note])
    })
    })
    return found


    I've wrote a little working example here


    as Iavra mentioned a for loop may be faster most of the time, I think though it's meaningful to know all the possibility and be aware of all the Array extras methods as they are very easy to work with and you can still optimise later if needed, also is helpful to understand what other people's code does.
     
    #8
    DoubleX likes this.

Share This Page