115

I have this:

map = ranks.map((row, r) => (
  row.map((rank, i) => {
    return [element(r, i, state, rank, toggled, onClick)];
  })
));

It maps through a 2-dimentional array. After each row, I'd like to insert <div class="clearfix"></div>.

I think, if I could somehow get the last index for each row, so I will be able to use it in the row map callback. Can someone show me how to do it?

Slavik Meltser
  • 9,712
  • 3
  • 47
  • 48
cocacrave
  • 2,453
  • 4
  • 18
  • 30

9 Answers9

197

Try something like:

row.map((rank, i, row) => {
  if (i + 1 === row.length) {
    // Last one.
  } else {
    // Not last one.
  }
})

Old answer:

const rowLen = row.length;
row.map((rank, i) => {
  if (rowLen === i + 1) {
    // last one
  } else {
    // not last one
  }
})
Top-Master
  • 7,611
  • 5
  • 39
  • 71
LeoYuan 袁力皓
  • 2,270
  • 1
  • 15
  • 16
159

As LeoYuan answered, this is the correct answer, but it can be a bit improved.
map accepts a function with a third parameter, which is the iterated array itself.

row.map((rank, i, arr) => {
    if (arr.length - 1 === i) {
        // last one
    } else {
        // not last one
    }
});

or in a bit shorter version, using an object destructuring (thanks Jose from the comments):

row.map((rank, i, {length}) => {
    if (length - 1 === i) {
        // last one
    } else {
        // not last one
    }
});

Using an arr.length instead of row.length is a better and correct approach for several reasons:

  1. When you mix scopes, it may lead for an unexpected bugs, especially in a poorly written or poorly designed code. In general, it is always a good way to avoid mixing between scopes when possible.

  2. When you like to provide an explicit array, it will work as well. E.g.

    [1,2,3,4].map((rank, i, arr) => {
        if (arr.length - 1 === i) {
            // last one
        } else {
            // not last one
        }
    });
    
  3. If you like to move the callback outside of the map scope (mainly for a better performance), it will be wrong to use row.length as it is out of scope. E.g. in the OP case:

    const mapElement = (rowIndex, state, toggled, onClick) => {
        return (rank, i, arr) => {
            let lastIndex = arr.length - 1;
            return [element(rowIndex, i, state, rank, toggled, onClick, lastIndex)];
        };
    };
    
    map = ranks.map((row, r) => row.map(mapElement(r, state, toggled, onClick)));
    
Slavik Meltser
  • 9,712
  • 3
  • 47
  • 48
  • 1
    Why do you add `arr` instead of using `row.length`? – Sanderfish Nov 08 '17 at 15:57
  • @Sanderfish Three reasons: 1. In general, I don't like to mix scopes, this way it makes it cleaner solution, especially when this code is part of a huge application; 2. This solution also works for explicitly defined arrays, e.g. `[1,2,3].map((rank, i, arr) => { ... })`; 3. If for whatever reason (for instance in a loop) you decide to move the callback outside of the scope, doing `row.length` will simply not work. I will update my answer to make it clear. – Slavik Meltser Nov 19 '17 at 21:14
  • 28
    You can also do `.map((rank, i, { length }) => { ... })` – Jose Hermosilla Rodrigo Nov 19 '17 at 21:51
  • Agree yours is better approach – Black Mamba Jan 09 '18 at 13:04
  • 4
    Correct me if I'm wrong but it seems this is also better approach for React JSX since you can't just declare a variable inline? – Alesh Houdek Feb 08 '19 at 10:09
21

Fewer lines of code with the same results

row.map((rank, i, {length}) => (

  //last element
  if(i + 1 === length){ 

  }
));
Nasser Ali Karimi
  • 4,462
  • 6
  • 34
  • 77
5

A shorter method would be to use .map combined with ternary operator, like this.

const positions = ["first", "second", "third", "fourth"]

positions.map((x, index, array) => {
    index === array.length -1 
        ? console.log("this is the last item in the array")
         : console.log( x)
}

//////////// explanation

x ### returns the current element .map is looping through

index ### returns the index(location of item in an array) of the current element.

array ### return the same element we are looping through so if we use sth like this

 ["first", "second", "third", "fourth"].map...

we'll still get the array we're looping through

array.length - 1 ### gives us the length of the array and - 1 gives us the index of the last element in the array.

Nimantha
  • 6,405
  • 6
  • 28
  • 69
Chukwuemeka Maduekwe
  • 6,687
  • 5
  • 44
  • 67
4

A slight improvement on the accepted answer:

const lastIndex = row.length - 1;
row.map((rank, i) => {
  if (i === lastIndex) {
    // last one
  } else {
    // not last one
  }
})

This removes the arithmetic from inside the loop.

tpower
  • 56,100
  • 19
  • 68
  • 100
  • 5
    This will not work if the array's length dynamically changes during the runtime (which happens in most of the cases). That is why you must check array's length each end every time. Note, that `row.length` doesn't degrade the performance, as it doesn't count the items every time you request it, but stores the length as a parameter in the `Array` instance. – Slavik Meltser Mar 22 '18 at 15:19
2

simplify answer above

const array = ['apple','orange','banana'];

array.map((element, index) => (index === array.length - 1) ? \`${element}.\` : \`${element},\`);
Rajan Sharma
  • 2,211
  • 3
  • 21
  • 33
Ssylur
  • 19
  • 4
1

you can check last index with your array's length. here is a logic

var randomnumber = Math.floor(Math.random() * (100 - 10 + 1)) + 10

console.log("your last index is dynamic, ehich is ",randomnumber-1);
let arry = [];
for (i=1;i<randomnumber;i++){
    arry.push(i)
}

    arry.map((data,index)=>{
          if(index == arry.length-1 ){
            console.log("last index data  ",data)
          }
          else{
          console.log("remain data ",data)
          
          }

    })

    console.log("your last index is dynamic, which is ",randomnumber-1);
this is also works in dynamic arry changes.. it is a too simple technique which i use .. :-)
manan5439
  • 898
  • 9
  • 24
1
const array = ['apple','orange','banana'];

array.map((element, index) => {
  //Last element
  if (index === array.length - 1) {
    return `${element}.`;
  }
  //Another elements
  return `${element}, `;
})}

Will return apple, orange, banana.

Hadnazzar
  • 1,517
  • 19
  • 21
0

Perhaps the most concise way (although a little "dirty" – you can get some ESLint errors and TypeScript also might not be happy about that) to access the length property in array.map() is to pull it out (by destructuring) of the third callback argument (which is the array we are mapping over) and then assign a new property e. g. lastIndex, which value is being derived from that previously pulled out length:

let list = ["Alice", "Bob", "Cedrick", "David", "Emily"]

let mapped = list.map((item, i, {length, lastIndex = length - 1}) => {
  return i === lastIndex ? "lastitem: " + item : item
})

console.log(mapped)
HynekS
  • 2,738
  • 1
  • 19
  • 34