Nicholas Hazel
Nicholas Hazel

Reputation: 3750

Node - Access last index of multidimensional arrays

I have a question that I believe is straightforward, but I've seen conflicting answers on.

Let's say I have the following data structure:

const data = [
  ...,
  {
    someKey: [
      ...,
      {
        someDeepKey: [
          ...,
          'two'
        ],
      }
    ]
  }
]

So accessing the first index of all of the following would look like this:

data[0].someKey[0].someDeepKey[0]

But if I wanted to find the LAST index of all of these, I haven't found a clean method. I have seen some people recommend extending the Array prototype itself, which I feel is a bad idea.

The only other "clean" solution I can think of is to chain down variables to make it semi readable.

let someKey = data[data.length - 1].someKey
let someDeepKey = someKey[someKey.length - 1].someDeepKey
let someDeepKeyValue = someDeepKey[someDeepKey.length - 1]

Can anybody suggest a cleaner way to do this? I believe Python supports something neat such as:

data[-1].someKey[-1].someDeepKey[-1]

and I was wondering if there is anything similar in JS.

Upvotes: 3

Views: 66

Answers (4)

Nicholas Hazel
Nicholas Hazel

Reputation: 3750

I did happen to stumble upon this. It appears to do what I want without mutating the original array, much cleaner than doing the whole .length - 1 chain:

let data = [
  {
    someKey: [
      {
        someOtherKey: [
          'Fake',
          'Name'
        ]
      }
    ]
  }
];

let name = data.slice(-1).pop()
  .someKey.slice(-1).pop()
  .someOtherKey.slice(-1).pop();

console.log(name) // 'Name'
console.log(data) // Original data

Unsure how performant it is, but its certainly at least a lot more readable and it grabs the end values needed.

Probably a performance nightmare since its making so many copies in memory, but when we're just dealing with smaller datasets, I think this fits the bill to snag accurate values in deeply nested arrays without mutating anything.


Ran some evaluations. Here are some basic tests here:

// Scaffold a dataset
let dataSet = [];
for (let i = 0; i < 100; i++) {
  let someObj = {
    someKey: []
  }
  for (let j = 0; j < 100; j++) {
    let someOtherObj = {
      someOtherKey: []
    }
    for (let k = 0; k < 1000; k++) {
      someOtherObj.someOtherKey.push(k)
    }
    someObj.someKey.push(someOtherObj)
  }
  dataSet.push(someObj)
}

const slicePop = (data) => {
  // Slice Pop
  let start = window.performance.now();
  let name = data.slice(-1).pop()
    .someKey.slice(-1).pop()
    .someOtherKey.slice(-1).pop();
  let end = window.performance.now();
  let time = end - start;
  return time
}

const spreadPop = (data) => {
  // Spread Pop
  let start = window.performance.now();
  let name = [...[...[ ...data ].pop().someKey].pop().someOtherKey].pop()
  let end = window.performance.now();
  let time = end - start;
  return time
}

const lastMethod = (data) => {
  // Function
  let start = window.performance.now();
  const last = (arr) => arr[arr.length - 1]
  let name = last(last(last(data).someKey).someOtherKey)
  let end = window.performance.now();
  let time = end - start;
  return time
}

const variables = (data) => {
  let start = window.performance.now();
  let someKey = data[data.length - 1].someKey
  let someOtherKey = someKey[someKey.length - 1].someOtherKey
  let name = someOtherKey[someOtherKey.length - 1]
  let end = window.performance.now();
  let time = end - start;
  return time
}

const lastDeep = (arr) => {
  let start = window.performance.now();
  let name = ['someKey', 'someOtherKey', ''].reduce(
    (last, cur) => (
      (res = last[last.length - 1]),
      res[cur] ? res[cur] : undefined
    ),arr)
  let end = window.performance.now();
  let time = end - start;
  return time
}

let slicePopTimes = [];
let spreadPopTimes = [];
let lastMethodTimes = [];
let variablesTimes = [];
let reduceTimes = [];

for (let i = 0; i < 100; i++) {
  slicePopTimes.push(slicePop(dataSet))
}

for (let i = 0; i < 100; i++) {
  spreadPopTimes.push(spreadPop(dataSet))
}

for (let i = 0; i < 100; i++) {
  lastMethodTimes.push(lastMethod(dataSet))
}

for (let i = 0; i < 100; i++) {
  variablesTimes.push(variables(dataSet))
}

for (let i = 0; i < 100; i++) {
  reduceTimes.push(lastDeep(dataSet))
}

const getAvg = (arr) => {
    let total = arr.reduce((acc, i) => {
        acc += i
    return acc
    }, 0);
    return ((total / arr.length) * 1000)
}

let results = {
  slicePopTime: getAvg(slicePopTimes),
  spreadPopTime: getAvg(spreadPopTimes),
  lastMethodTime: getAvg(lastMethodTimes),
  variablesTime: getAvg(variablesTimes),
  reduceTime: getAvg(reduceTimes)
}
console.log('RESULTS', results)

Upvotes: 2

Siva Kondapi Venkata
Siva Kondapi Venkata

Reputation: 11001

Here small util using reduce, will be handy and works for any level of nesting.

const data = [
  {
    someKey: [
      {
        someDeepKey: ["one"],
      },
    ],
  },
  {
    someKey: [
      {
        someDeepKey: ["one", "two"],
      },
    ],
  },
];

const lastDeep = ["someKey", "someDeepKey", ""].reduce(
  (last, cur) => ((res = last[last.length - 1]), res[cur] ?? res),
  data
);

console.log(lastDeep);

Upvotes: 2

hithacker
hithacker

Reputation: 151

Define a function named last to get last element of an array and use it like this last(last(last(data).someKey).someDeepKey).

Upvotes: 1

Mohammad Yaser Ahmadi
Mohammad Yaser Ahmadi

Reputation: 5051

you can following this code

let x = [...[...[ ...data ].pop().someKey].pop().someDeepKey].pop()

Upvotes: 1

Related Questions