Yes, V8 has optimised string slicing to O(1). This does of course depend a lot on what else happens to all the strings, they might need to get copied later on.

The relevant implementation from the above link is:

// The Sliced String class describes strings that are substrings of another
// sequential string.  The motivation is to save time and memory when creating
// a substring.  A Sliced String is described as a pointer to the parent,
// the offset from the start of the parent string and the length.  Using
// a Sliced String therefore requires unpacking of the parent string and
// adding the offset to the start address.  A substring of a Sliced String
// are not nested since the double indirection is simplified when creating
// such a substring.
// Currently missing features are:
//  - handling externalized parent strings
//  - external strings as parent
//  - truncating sliced string to enable otherwise unneeded parent to be GC'ed.
class SlicedString: public String {
  // ...
};

Also beware of your quick test results. As you are doing nothing with the y variable, the slicing and even the whole loop might get eliminated as dead code by the optimiser. If you are doing benchmarks, do them on practical real world data.

Answer from Bergi on Stack Overflow
Top answer
1 of 2
7

(V8 developer here.)

Array.prototype.slice is O(n), where n is the number of elements in the slice.
String.prototype.slice is O(1), thanks to our implementation of SlicedStrings, which are just storing pointer, offset, length to the original string and avoid copying the characters (except when they're tiny, so that copying a handful of characters is actually cheaper and smaller than storing a reference; that's still O(1)).

The key difference is that strings are immutable, and arrays are not. When you do str1 = "Hello World"; str2 = str1.slice(2, 5);, since there is no way to modify str1's contents afterwards, str2 doesn't need to ensure that it's unaffected by any such modification.
When you do a = [1, 2, 3, 4]; b = a.slice(1, 3); a[1] = "changed"; console.log(b[0]);, then you expect to see 2, not "changed". That's why b has to be an actual copy. (In theory, a copy-on-write approach would be possible, but V8 doesn't do that for array slices.)

"Shallow copy" means that nested objects will not be copied. Example:

let nested = {property: "value"};
var a = [nested];
var b = a.slice(0, 1);
a[0].property = "new value";
console.log(a === b);          // false, `b` is a copy
console.log(a[0] === b[0]);    // true, `nested` was not copied
console.log(b[0] === nested);  // true
console.log(b[0].property);    // "new value"
2 of 2
0

Based on my read (which is to say, I could be wrong, since V8 is a complicated beast) of this array-slice.tq source code, the answer is: "it depends".

If possible (and the heuristics as to when that might happen I didn't really get to), V8 optimizes things to essentially O(1) by just returning a copy-on-write view to the original array via ExtractFastJSArray.

When that fails, V8 allocates a new array and copies object (pointers) over, which is of course O(N).

The tq source code includes lots of "gotcha" cases, since JavaScript does allow you to call Array.prototype.slice() on things that aren't really arrays.

Discussions

javascript - What's the time complexity of array.splice() in Google Chrome? - Stack Overflow
Similar things can be done by Javascript interpreters. ... Find the answer to your question by asking. Ask question ... See similar questions with these tags. ... Live from re:Invent…it’s Stack Overflow! ... 2 The splice method causes the for loop to be executed n - 2 times (n is the number of elements in an array) 0 Time complexity of slice ... More on stackoverflow.com
🌐 stackoverflow.com
javascript - Performance question: String.split and then walk on the array, or RegExp? - Stack Overflow
I'll do some work on a line separated string. Which one will be faster, to split the text via String.split first and then walk on the resultant array or directly walk the whole text via a reg exp and More on stackoverflow.com
🌐 stackoverflow.com
JavaScript runtime complexity of Array functions - Stack Overflow
Is the runtime complexity defined by the JS standard on common Array functions like push, pop, shift, slice or splice? Esp. I'm interested in removing and inserting entries at random positions. If ... More on stackoverflow.com
🌐 stackoverflow.com
What is the time complexity of forEach? of slice?
So, a O(n) solution will increase linearly as the size of the input increases. I would solve that problem by getting the product of the entire array, to do this you have to touch each element in the array, so that's O(n). Then, I'd go over the array again and divide the total product by each element in the array, this is also O(n). So, this solution would be O(2n), not O(n2 ). With Big O, you drop constants, because in the big picture, they don't matter. An array of length 10 with take twice as long as array of length 5. If it was O(n2 ), length 10 would take 4x as much as a length of 5. For methods like map or reduce, they are the same as forEach. They go over a collection and perform one operation per item. For methods like slice or indexOf, we can't say exactly what the time complexity is, because it could stop at the first index or the last. It's fine to chain methods, because they're ran asynchronously, not concurrently. One method doesn't run until the previous one completes. What you need to be careful of is nesting. Two nested for loops with an indexOf inside, could get expensive for example. More on reddit.com
🌐 r/learnjavascript
6
1
June 22, 2016
🌐
JavaScript in Plain English
javascript.plainenglish.io › understanding-time-and-space-complexity-of-common-javascript-built-in-methods-39a3285a6409
Understanding Time and Space Complexity of Common JavaScript Built-in Methods | by Kyle Le | JavaScript in Plain English
April 14, 2023 - String.prototype.slice(): O(n) time complexity and O(n) space complexity. This method extracts a section of a string and returns it as a new string. String.prototype.split(): O(n) time complexity and O(n) space complexity.
🌐
MDN Web Docs
developer.mozilla.org › en-US › docs › Web › JavaScript › Reference › Global_Objects › String › slice
String.prototype.slice() - JavaScript - MDN Web Docs
For example, str.slice(4, 8) extracts the fifth character through the eighth character (characters indexed 4, 5, 6, and 7): indexStart indexEnd ↓ ↓ | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | | T | h | e | | m | i | r | r | o | r | m i r r _______________ ↑ Result · If indexStart >= str.length, an empty string is returned.
🌐
Medium
audreyhal.medium.com › reverse-a-string-in-javascript-plus-time-complexities-8cef62f4110e
Reverse a String in JavaScript (Checking Time Complexities) | by Chioma Halim | Medium
July 20, 2021 - // Time Complexity = O(n)function reverseString(string) { if (string.length === 1) return string; let parentNode = Math.floor(string.length / 2); let leftPart = string.substring(0, parentNode) let rightPart = string.substring(parentNode); return ...
🌐
Hashnode
hashnode.com › post › time-complexity-of-substr-method-in-javascript-cjjqj3qft012i3ks1ih811u19
Time complexity of substr method in javascript? - Hashnode
July 18, 2018 - So for the following code, which ... let current = str.substr(i, k); rtn.push(current) } return rtn; } the time complexity is O(n*k), which is approximated to O(n), right?...
🌐
AlgoCademy
algocademy.com › link
Time Complexity Practice 2 in JavaScript | AlgoCademy
To solve this problem, we need to understand the time complexity of slicing and concatenation operations in JavaScript. Let's start with a naive solution and then move on to more optimized approaches. A naive solution would involve using the slice method to create subarrays and the concat method ...
🌐
MDN Web Docs
developer.mozilla.org › en-US › docs › Web › JavaScript › Reference › Global_Objects › String › substring
String.prototype.substring() - JavaScript - MDN Web Docs
slice() also treats NaN arguments as 0, but when it is given negative values it counts backwards from the end of the string to find the indexes.
Find elsewhere
🌐
Medium
medium.com › @sonu9506517825 › string-methods-in-javascript-155f216e4540
String Methods in JavaScript. 1. length : time complexity O(N) |… | by Sonu Verma | Medium
December 26, 2022 - String Methods in JavaScript 1. length : time complexity O(N) | space complexity O(N) calculate the number of element in string return count of element in string 2. at() : time complexity O(1) | …
Top answer
1 of 4
37

Worst case should be O(n) (copying all n-1 elements to new array).

A linked list would be O(1) for a single deletion.

For those interested I've made this lazily-crafted benchmark. (Please don't run on Windows XP/Vista). As you can see from this though, it looks fairly constant (i.e. O(1)), so who knows what they're doing behind the scenes to make this crazy-fast. Note that regardless, the actual splice is VERY fast.

Rerunning an extended benchmark directly in the V8 shell that suggest O(n). Note though that you need huge array sizes to get a runtime that's likely to affect your code. This should be expected as if you look at the V8 code it uses memmove to create the new array.

2 of 4
7

¡Hi!

I did an experiment myself and would like to share my findings. The experiment was very simple, we ran 100 splice operations on an array of size n, and calculate the average time each splice function took. Then we varied the size of n, to check how it behave.

This graph summarizes our findings for big numbers:

For big numbers it seems to behave linearly.

We also checked with "small" numbers (they were still quite big but not as big):

On this case it seems to be constant.

If I would have to decide for one option I would say it is O(n), because that is how it behaves for big numbers. Bear in mind though, that the linear behaviour only shows for VERY big numbers.

However, It is hard to go for a definitive answer because the array implementation in javascript dependes A LOT on how the array is declared and manipulated.

I recommend this stackoverflow discussion and this quora discussion to understand how arrays work.

I run it in node v10.15.3 and the code used is the following:

const f = async () => {
  const n = 80000000;
  const tries = 100;
  const array = [];
  for (let i = 0; i < n; i++) { // build initial array
    array.push(i);
  }
  
  let sum = 0;
  for (let i = 0; i < tries; i++) {
    const index = Math.floor(Math.random() * (n));
    const start = new Date();
    array.splice(index, 1); // UNCOMMENT FOR OPTION A
    // array.splice(index, 0, -1); // UNCOMMENT FOR OPTION B
    const time = new Date().getTime() - start.getTime();
    sum += time;
    array.push(-2); // UNCOMMENT FOR OPTION A, to keep it of size n
    // array.pop(); // UNCOMMENT FOR OPTION B, to keep it of size n

  }
  console.log('for an array of size', n, 'the average time of', tries, 'splices was:', sum / tries);
 };
f();

Note that the code has an Option B, we did the same experiment for the three argument splice function to insert an element. It worked similary.

🌐
Medium
hemanthkollanur.medium.com › optimisation-issues-with-array-slice-b075e7a6e5e7
Optimisation issues with Array.slice() | by Hemanth KV | Medium
July 17, 2023 - 2. Performance: The `Array.slice()` method has a time complexity of O(n), where n is the number of elements in the resulting slice. If you’re working with large arrays and frequently slicing them, the performance impact can be noticeable.
🌐
ITNEXT
itnext.io › decoding-big-o-notation-time-and-space-complexities-in-javascript-d31ddb8e1254
Decoding Big O Notation Time and Space Complexities in JavaScript | ITNEXT
August 3, 2023 - Time Complexity: O(n) — as it converts the string to an array, reverses the array, and joins it back to a string.
Top answer
1 of 3
185

The ECMA specification does not specify a bounding complexity, however, you can derive one from the specification's algorithms.

push is O(1), however, in practice it will encounter an O(N) copy costs at engine defined boundaries as the slot array needs to be reallocated. These boundaries are typically logarithmic.

pop is O(1) with a similar caveat to push but the O(N) copy is rarely encountered as it is often folded into garbage collection (e.g. a copying collector could only copy the used part of an array).

shift is at worst O(N) however it can, in specially cases, be implemented as O(1) at the cost of slowing down indexing so your mileage may vary.

slice is O(N) where N is end - start. Not a tremendous amount of optimization opportunity here without significantly slowing down writes to both arrays.

splice is, worst case, O(N). There are array storage techniques that divide N by a constant but they significantly slow down indexing. If an engine uses such techniques you might notice unusually slow operations as it switches between storage techniques triggered by access pattern changes.

One you didn't mention, is sort. It is, in the average case, O(N log N). However, depending on the algorithm chosen by the engine, you could get O(N^2) in some cases. For example, if the engine uses QuickSort (even with an late out to InsertionSort), it has well-known N^2 cases. This could be a source of DoS for your application. If this is a concern either limit the size of the arrays you sort (maybe merging the sub-arrays) or bail-out to HeapSort.

2 of 3
17

in very simple words

push -> O(1)

pop -> O(1)

shift -> O(N)

slice -> O(N)

splice -> O(N)

Here is a complete explanation about time complexity of Arrays in JavaScript.

🌐
Reddit
reddit.com › r/learnjavascript › what is the time complexity of foreach? of slice?
r/learnjavascript on Reddit: What is the time complexity of forEach? of slice?
June 22, 2016 -

I randomly happened upon someones Github page where he shared some of his algorithm practice problems and solutions.

One of the problems conditions stated we are given an array of all integers. For each index of that array, find the product of every other index. Solve this in linear time, linear space

Example input: [2, 7, 1, 4] Expected output: [28, 8, 56, 14]

The weird thing was, he used 2 forEach loops to come to his solution. He used both forEach loops to iterate over the array and he had variables to store product and eventually push into a final array that he returns at the end.

This had me questioning the time complexity of forEach. I found this explanation on ecma-international.org. Basically it shows O(n) time complexity for a single forEach loop. Please correct me if I'm wrong.

So, the persons solution should be O(n2) right? I really just want confirmation from someone with more experience so that I don't keep questioning myself on this..

This got me thinking about using other built-in methods. I've had solutions where I've used multiple slice methods or enumerables like map or reduce. In these cases, what is the expected time complexity of using a single slice method or map method? Isn't it really bad once you start method chaining?

🌐
Medium
medium.com › @avinashkumar151199 › slice-and-splice-in-javascript-30e81e4ea997
Slice and Splice in JavaScript?. slice()-: Slice is used to get a new… | by Avinash Kumar | Medium
August 10, 2023 - In b.slice(2, 4) means that you want to extract elements from index 2 up to, but not including, index 4. Since arrays are zero-indexed, this range includes elements at indices 2 and 3. So, the extracted portion is ['Apple', 'Pine-Apple']. splice()-: Splice is used to add/remove an element from the given array, it change the original array. The time complexity of spliceis O(n).
🌐
GeeksforGeeks
geeksforgeeks.org › javascript-string-slice
JavaScript string.slice() Method - GeeksforGeeks
May 15, 2023 - The string.slice() is an inbuilt method in javascript that is used to return a part or slice of the given input string.
🌐
Reddit
reddit.com › r/learnpython › algorithm complexity with strings and slices
r/learnpython on Reddit: Algorithm complexity with strings and slices
December 1, 2019 -

Recently I was thinking about interview questions I got as an undergrad:
Things like "reverse a string" and "check if a string is a palindrome".

I did most of these in C++ with a loop and scrolling through the index using logic.

When I learned Python, I realized that I could "reverse a string" by simply going:

return mystring[::-1]

Likewise with "check if it is a palindrome" by doing:

return mystring == mystring[::-1]

The problem now is that, I don't know what kinda complexity it is.

From my point of view, it is constant, so O(1). But I am guessing that that is too good to be true as the string splicing is doing something behind the scenes.

Can anyone help me clarify?

Top answer
1 of 2
2

The python page on time-complexity shows that slicing lists has a time-complexity of O(k), where "k" is the length of the slice. That's for lists, not strings, but the complexity can't be O(1) for strings since the slicing must handle more characters as the size is increased. At a guess, the complexity of slicing strings would also be O(k). We can write a little bit of code to test that guess:

import time

StartSize = 2097152

size = StartSize
for _ in range(10):
    # create string of size "size"
    s = '*' * size

    # now time reverse slice
    start = time.time()
    r = s[::-1]
    delta = time.time() - start

    print(f'Size {size:9d}, time={delta:.3f}')

    # double size of the string
    size *= 2

This uses a simple method of timing. Other tools exist, but this is simple. When run I get:

$ python3 test.py
Size   2097152, time=0.006
Size   4194304, time=0.013
Size   8388608, time=0.024
Size  16777216, time=0.050
Size  33554432, time=0.098
Size  67108864, time=0.190
Size 134217728, time=0.401
Size 268435456, time=0.808
Size 536870912, time=1.610
Size 1073741824, time=3.192

which shows the time doubles when doubling the size of the string for each reverse slice. So O(n) (k == n for whole-string slicing).

Edit: spelling.

2 of 2
1

How difficult an algorithm is to write and how difficult it is to calculate are two separate things. Creating a reversed string with the shorthand still requires n order space and n order time. Keep in mind that, in most cases, creating a reversed array isn't necessary, you can just start at the top and go down, which is essentially what Python's reversed() function does