130

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?

1
  • I don't understand why this code wraps the result of element in an array, so that the final output is actually a 3-dimensional array. Commented Nov 14, 2024 at 15:50

9 Answers 9

215

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
  }
})
Sign up to request clarification or add additional context in comments.

Comments

167

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)));
    

5 Comments

Why do you add arr instead of using row.length?
@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.
You can also do .map((rank, i, { length }) => { ... })
Agree yours is better approach
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?
28

Fewer lines of code with the same results

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

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

  }
));

2 Comments

This is the best solution out there!
this works nicely when you're already piping that can change the size.
9

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.

1 Comment

I really like the readability on that ternary!
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.

1 Comment

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.
2

simplify answer above

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

array.map((element, index) => (index === array.length - 1) ? \`${element}.\` : \`${element},\`);

Comments

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 .. :-)

1 Comment

i found this is an optimal solution. thank you buddy.
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.

Comments

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)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.