1

The following is not valid ES6 syntax, but perhaps there's a simple way to accomplish the same thing, e.g., using Ramda?

const [...butLast, last] = values
Eric Walker
  • 7,063
  • 3
  • 35
  • 38

4 Answers4

2

If you're using ramda you can do this

const values = [1, 2, 3]
const [butLast, Last] = [R.dropLast(1, values), R.last(values)]

R.dropLast(n, arr): returns a copy of array arr with the last n elements removed R.last(arr): returns the last element in an array

gafi
  • 12,113
  • 2
  • 30
  • 32
1

You can reverse the array, use a standard ...rest operator, and then reverse the rest back:

const initAndLast = (arr) => (([last, ...butLast]) => [butLast.reverse(), last])([...arr].reverse())

const [bl1, l1] = initAndLast([1, 2, 3]);

console.log(`butLast: ${JSON.stringify(bl1)}, last: ${l1}`)

const [bl2, l2] = initAndLast([1]);

console.log(`butLast: ${JSON.stringify(bl2)}, last: ${l2}`)

With ramda, you can use R.init and R.last with R.juxt:

const initAndLast = R.juxt([R.init, R.last]);

const [bl1, l1] = initAndLast([1, 2, 3]);

console.log(`butLast: ${JSON.stringify(bl1)}, last: ${l1}`)

const [bl2, l2] = initAndLast([1]);

console.log(`butLast: ${JSON.stringify(bl2)}, last: ${l2}`)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>
Ori Drori
  • 183,571
  • 29
  • 224
  • 209
  • 1
    The `butLast` part is straightforward. Is there a simple way to also get the `last` value? Note that it would be `null` in case of a 1 element array. (I _think_.) – Eric Walker Jan 22 '18 at 20:55
  • You can get the last element with slice, but it will not be `null` if the array contains only 1 element. – Ori Drori Jan 22 '18 at 20:57
  • 1
    Minor note for the OP, you'll need a `[0]` since `last` is actually an array holding the last value, not the last value itself. – apsillers Jan 22 '18 at 20:57
  • I take that back. For `const [first, ...rest] = values`, in the case of a 1 element array, `first` will contain a value and `rest` will be an empty array. If the opposite behavior is followed, then `butLast` would be empty and `last` would be a scalar value containing the single element. – Eric Walker Jan 22 '18 at 21:03
  • I thought about this myself, and it's a good solution, so I've upvoted it. (One cannot accept several answers, unfortunately.) – Eric Walker Jan 22 '18 at 21:25
  • Note that `arr.reverse()` reverses an array in place, which is probably not something you'd want to do to an argument passed into a function in most cases. Something like `R.reverse` is probably better. – Eric Walker Jan 22 '18 at 21:30
  • A simple cloning of the array solves that. Good catch. – Ori Drori Jan 22 '18 at 21:32
0

You can use Array.prototype.slice(), Array.prototype.pop()

let values = [1,2,3,4,5];
// copy `values` array using `.slice()`, `.pop()` the copy
// alternatively `values.pop()` to remove last element from `values`
const [butLast, last] = [values, values.slice(0).pop()];

console.log(butLast, last);
guest271314
  • 1
  • 15
  • 104
  • 177
  • Actually, using slice leaves the last element in the array. I think `butLast` should be everything _but_ the last element. Removing the call to slice alleviates this. +1 because this is the accepted solution without the use of a library. –  Jan 23 '18 at 04:55
  • @TinyGiant Was not sure if `values` was expected to be changed by the procedure. – guest271314 Jan 23 '18 at 05:00
  • Well yeah, there is that. tough one. –  Jan 23 '18 at 05:20
0

You're looking at the problem the wrong way. You don't want the rest operator at the beginning of the destructuring assignment. What you want is to get the elements from the end of the array instead of the beginning. The position of the rest operator is irrelevant to the desired result.

There are two concepts that exist in the object destructuring portion of destructuring assignments that together will allow you to accomplish what you really want using only what the language provides.

These two concepts are:

  1. Assigning values of properties to identifiers whose names differ from that of the property.

    const { thing: otherthing } = { thing: 1 };
    console.log(otherthing, thing); // 1, undefined
    
  2. Access properties dynamically:

    const prop = 'thing';
    const { [prop]: otherthing } = { thing: 1 };
    console.log(otherthing, thing); // 1, undefined
    

These two concepts combined with the fact that arrays are objects, allows you to do the following:

const values = [1,2,3,4,5,6,7,8,9];
const { [values.length - 1]: last, ...rest } = values;
console.log(last, Object.values(rest)); // 9 [1,2,3,4,5,6,7,8]

The downsides being:

  • You have to know the length of the array, so you have to store the array before destructuring, or include the length property in the destructuring assignment:

    const { length, [length - 1]: last, ...rest } = [1,2,3,4,5,6,7,8,9];
    console.log(last, Object.values(rest)); // 9 [1,2,3,4,5,6,7,8]
    
  • If you want the result of the rest assignment to be a true array you have to use Object.values() as above to convert the result of the rest assignment back to an array, but you could wrap that in an IIFE to prevent scope interference (this also prevents the scope intereference of the previous downside):

    const { last, rest } = (({ length: l, [l-1]: last, ...rest } = [1,2,3,4,5,6,7,8,9]) =>
                            ({ last, rest: Object.values(rest) }))();
    console.log(last, rest); // 9 [1,2,3,4,5,6,7,8]