0

I wish I can create a function that will help me to get the last item from an array, NOT just from a normal array, but from other array types such as NodeList, (the only two types I know are NodeLists and Arrays)

my function:

       const getLastItem = (list) => {
            if (list instanceof Array || OR NODELIST OR ANYTHING ELSE) {
                return list.slice(-1)[0];
            }
            if(typeof list == 'object'){
           // get the last key from an object, it's another thing which I wish to append to this
           // function
              return Object.keys(list).slice(-1)[0]
           //this will return the last key from an object
            }
        }

[EDIT]: and do you think that there is a better implementation than this one?

12
  • 1
    function isIterable(obj) { return typeof obj[Symbol.iterator] === 'function'; } Commented Aug 27, 2020 at 11:29
  • Does "last key from an Object" actually make sense? Is there a defined order that would mean that "last" had any useful meaning? Commented Aug 27, 2020 at 11:30
  • @ritaj, sorry, could you please explain? Commented Aug 27, 2020 at 11:30
  • 2
    First convert your iterable to an array then return the last element. Commented Aug 27, 2020 at 11:33
  • 1
    I guess it shouldn't make any real difference, but I try to avoid additional function calls when possible. Too bad JS never got [-1] syntax. Commented Aug 27, 2020 at 11:39

1 Answer 1

2

Something like this? The function grabs the last item from iterables and calls itself recursively on Object.entries of non-iterable objects. It returns undefined if it can't do either (or if the iterable is empty).

const getLastItem = obj => {
    if (obj && obj[Symbol.iterator]) {
        const arr = [...obj]

        return arr[arr.length - 1]
    } else if (obj && typeof obj === 'object') {
        // You could also use `Object.keys` or `Object.values`
        // depending what you want to happen.
        // Note that relying on the order of properties
        // in normal objects is typically a bad idea.
        return getLastItem(Object.entries(obj))
    }
    
    return undefined
}

;[
    // all of the following work
    getLastItem([1, 2]),
    getLastItem({a: 1, b: 2}),
    getLastItem(document.querySelectorAll('p')),
    getLastItem(new Set([1, 2])),
    getLastItem(new Map([['a', 1], ['b', 2]])),
    getLastItem(document.querySelector('p').classList),
    getLastItem('string!'),
    
    // all of the following are `undefined`
    getLastItem(null),
    getLastItem(undefined),
    getLastItem(0),
    getLastItem(1),
    getLastItem(document.querySelectorAll('fake-element')),
    getLastItem([]),
    getLastItem({}),
    getLastItem(''),
    getLastItem(new WeakMap([[{}, 1], [{}, 2]])), // not iterable
].forEach(x => console.log(x))

// Edge case: if `obj` is an infinite iterable, you'll get
// stuck in an infinite loop due to use of the spread operator...
// So don't do this 😉
// 
// const infiniteIterable = (n => ({
//     *[Symbol.iterator] () {
//         while(true) yield n++
//     },
// }))(0)
// 
// getLastItem(infiniteIterable)
<p class="class1 class2 class3">first</p>

<p>second</p>

<p>last</p>

Sign up to request clarification or add additional context in comments.

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.