0

I am trying to find an efficient way of changing the length of an array and the elements within the array. For example, if I have

var arr = [["Name", "Age", "Lightsaber Color"], ["Luke Skywalker", "22", "Green"], ["Yoda", "900", "Green"], ["Obi Wan Kenobi", "59", "Blue"]]

I want to end up with

var arr = [["Name", "Age"], ["Luke Skywalker", "22"], ["Yoda", "900"]]

I have written some code to do this, but I want to ensure that the way I am changing the length of the nested arrays (for lack of a better term) is as efficient as possible.

I have:

var arr = [["Name", "Age", "Lightsaber Color"], ["Luke Skywalker", "22", "Green"], ["Yoda", "900", "Green"], ["Obi Wan Kenobi", "59", "Blue"]]
arr.length = 3; // Removes the Obi Wan entry
console.log(arr);
for(var i = 0; i < arr.length; i++){
        arr[i].length = 2 // Removes lightsaber color
};
console.log(arr)

The code I'm wanting to hopefully optimize is the for loop. I'll be working with much larger datasets. Thanks

Palps
  • 568
  • 9
  • 20
  • 4
    *"Something something... premature optimization"* - Have you tried your code already? I don't see any glaring inefficiencies such that you need to preemptively fix it. – Tyler Roper Jan 28 '20 at 14:11
  • You can't optimize it more. You have to atleast go for a single for loop. – Maheer Ali Jan 28 '20 at 14:12
  • *"I'll be working with much larger datasets"* do you have idea of how much large it could be? If it wont be really, really large, I don't see reasons to care about optimization here – Calvin Nunes Jan 28 '20 at 14:14
  • No, 100,000 would most likely be an upper limit. – Palps Jan 28 '20 at 14:16
  • If you have to process that much data, you'll want to do it in chunks instead of inside one big for-loop. That'll be way more important that the chosen syntax for the loop. One possible mini optimization inside the loop is caching the arr.length `for(var i = 0, length = arr.length; i < length; .......` – Shilly Jan 28 '20 at 14:18
  • I think, you can improve performance by avoiding to create a new array with new sub-arrays. Probably by having stream-like or consumer/producer like processing. But that heavily depends on how the two corresponding parts work and if they can be changed (the one that produced the source array and the one that consumes the reduced array). – isnot2bad Jan 28 '20 at 14:20

6 Answers6

6

The code I'm wanting to hopefully optimize is the for loop.

What you're doing is already as efficient as it's going to get, if you mean runtime efficiency. Anything using other things (like slice or map) is going to be slower.


Note that it's really unlikely to matter. Unless you've already identified that this loop is a performance blocker, don't worry about it. If you have identified that this is a performance blocker, the array(s) must be really big. :-) Even if you're dealing with 100,000 arrays, I wouldn't think this would take any significant time.

Let's find out:

const now = performance.now ? performance.now.bind(performance) : Date.now.bind(Date);

// Create an array with 110k entries, each being five entries long.
const arr = Array.from({length:110000}, () => ["a", "b", "c", "d", "e"]);

// Start timing
const start = performance.now();

// Cut it down to 100k entries
arr.length = 100000;

// Trim them to two entries
for (const entry of arr) {
    entry.length = 2;
}

// Done timing
const elapsed = now() - start;
console.log("Elapsed: " + elapsed + "ms");

I get about 20ms on my workstation using Brave (like Chrome, uses V8 as the JavaScript engine).

This is not a rigorous benchmark, but it gives us an idea...

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • 1
    Thanks. Im pretty new to javascript and not sure what performance considerations I need to be aware of. I'm throwing some data at it now. I asked the question because I've done something similar in c# and it was slowish and seem to chew up some memory. – Palps Jan 28 '20 at 14:20
0

Just a minor improvement, but sometimes Array.prototype.splice is faster than setting the array's length to truncate an array:

var arr = [["Name", "Age", "Lightsaber Color"], ["Luke Skywalker", "22", "Green"], ["Yoda", "900", "Green"], ["Obi Wan Kenobi", "59", "Blue"]]

arr.splice(3); // Removes the Obi Wan entry
console.log(arr);
for(var i = 0; i < arr.length; i++){
    arr[i].splice(2) // Removes lightsaber color
}
console.log(arr);

For a benchmark comparing both versions, see http://jsben.ch/BmvNj.

isnot2bad
  • 24,105
  • 2
  • 29
  • 50
  • I'm seeing only a ***very*** tiny benefit on V8 (Chrome, Brave, etc.), well within measurement error I'd say; a ***very*** slight detriment on JavaScriptCore (Safari), about the same as the benefit on V8; and a marked detriment on SpiderMonkey (Firefox). So for me, `length = x` is still the way to go. Interesting though, and a good example of "test your assumptions." – T.J. Crowder Jan 28 '20 at 14:54
  • @T.J.Crowder Well, on my notebook, `splice()` is about 10-20% faster on Chrome, but about 20% slower on Firefox. However, the benchmark is most probably not representative. So I rather go with your answer - it doesn't matter. – isnot2bad Jan 28 '20 at 15:10
  • Interesting! On V8 it was only about a +2% difference for me (vs. -13% on Firefox). Negligible either way, as you say. :-) – T.J. Crowder Jan 28 '20 at 15:13
-1

You can use array.splice:

arr[i] = arr[i].splice(0, 2);

Try this:

var arr = [["Name", "Age", "Lightsaber Color"], ["Luke Skywalker", "22", "Green"], ["Yoda", "900", "Green"], ["Obi Wan Kenobi", "59", "Blue"]]
arr.length = 3; // Removes the Obi Wan entry
console.log(arr);
for(var i = 0; i < arr.length; i++){
        arr[i] = arr[i].splice(0, 2);
};
console.log(arr)
Ricardo Rocha
  • 14,612
  • 20
  • 74
  • 130
-1

Well, you can do this:

arr.forEach(function(item) { item.splice(-1) });

splice(-1) modifies the original array, deleting the last item on it

Oscar Paz
  • 18,084
  • 3
  • 27
  • 42
-2

You can combine Array.map with Array.slice:

let arr = [["Name", "Age", "Lightsaber Color"], ["Luke Skywalker", "22", "Green"], ["Yoda", "900", "Green"], ["Obi Wan Kenobi", "59", "Blue"]];

let result = arr.map(person => person.slice(0, 2));

console.log(result);
isnot2bad
  • 24,105
  • 2
  • 29
  • 50
-2

Here is a simple and readable way of your requirements.

var arr = [["Name", "Age", "Lightsaber Color"], ["Luke Skywalker", "22", "Green"], ["Yoda", "900", "Green"], ["Obi Wan Kenobi", "59", "Blue"]]
    for(var i=0; i< arr.length;i++){
      arr[i].splice(-1, 1)
    }
    console.log(arr);
Shashikant
  • 103
  • 3