Why is array.push sometimes faster than array[n] = value? - javascript

As a side result of testing some code I wrote a small function to compare the speed of using the array.push(value) method vs direct addressing array[n] = value. To my surprise the push method often showed to be faster especially in Firefox and sometimes in Chrome. Just out of curiosity: anyone has an explanation for it?
Here's the test (note: rewritten 2023/02/10)
const arrLen = 10_000;
const x = [...Array(10)].map( (_, i) => testArr(arrLen, i));
console.log(`Array length: ${arrLen}\n--------\n${x.join(`\n`)}`);
function testArr(n, action) {
let arr = [];
const perfStart = performance.now();
const methods =
` for (; n; n--) arr.push(n)
for (; i < n; i += 1) { arr[i] = i; }
for (; i < n; i += 1) arr.push(i)
while (--n) arr.push(n)
while (i++ < n) arr.push(n)
while (--n) arr.splice(0, 0, n)
while (--n) arr.unshift(n)
while (++i < n) arr.unshift(i)
while (--n) arr.splice(n - 1, 0, n)
while (n--) arr[n] = n`.split(`\n`).map(v => v.trim());
const report = i => `${methods[i]}: ${
(performance.now() - perfStart).toFixed(2)} milliseconds`;
let i = 0;
switch (action) {
case 0: for (; n; n--) arr.push(n)
case 1: for (; i < n; i += 1) { arr[i] = i; } break;
case 2: for (let i = 0; i < n; i += 1) arr.push(i); break;
case 3: while (--n) arr.push(n); break;
case 4: while (i++ < n) arr.push(n); break;
case 5: while (--n) arr.splice(0, 0, n); break;
case 6: while (--n) arr.unshift(n)
case 7: while (++i < n) arr.unshift(i); break;
case 8: while (--n) arr.splice(n - 1, 0, n); break;
default: while (n--) arr[n] = n;
}
return report(action);
}
.as-console-wrapper {
max-height: 100% !important;
}

All sorts of factors come into play, most JS implementations use a flat array that converts to sparse storage if it becomes necessary later on.
Basically the decision to become sparse is a heuristic based on what elements are being set, and how much space would be wasted in order to remain flat.
In your case you are setting the last element first, which means the JS engine will see an array that needs to have a length of n but only a single element. If n is large enough this will immediately make the array a sparse array -- in most engines this means that all subsequent insertions will take the slow sparse array case.
You should add an additional test in which you fill the array from index 0 to index n-1 -- it should be much, much faster.
In response to #Christoph and out of a desire to procrastinate, here's a description of how arrays are (generally) implemented in JS -- specifics vary from JS engine to JS engine but the general principle is the same.
All JS Objects (so not strings, numbers, true, false, undefined, or null) inherit from a base object type -- the exact implementation varies, it could be C++ inheritance, or manually in C (there are benefits to doing it in either way) -- the base Object type defines the default property access methods, eg.
interface Object {
put(propertyName, value)
get(propertyName)
private:
map properties; // a map (tree, hash table, whatever) from propertyName to value
}
This Object type handles all the standard property access logic, the prototype chain, etc.
Then the Array implementation becomes
interface Array : Object {
override put(propertyName, value)
override get(propertyName)
private:
map sparseStorage; // a map between integer indices and values
value[] flatStorage; // basically a native array of values with a 1:1
// correspondance between JS index and storage index
value length; // The `length` of the js array
}
Now when you create an Array in JS the engine creates something akin to the above data structure. When you insert an object into the Array instance the Array's put method checks to see if the property name is an integer (or can be converted into an integer, e.g. "121", "2341", etc.) between 0 and 2^32-1 (or possibly 2^31-1, i forget exactly). If it is not, then the put method is forwarded to the base Object implementation, and the standard [[Put]] logic is done. Otherwise the value is placed into the Array's own storage, if the data is sufficiently compact then the engine will use the flat array storage, in which case insertion (and retrieval) is just a standard array indexing operation, otherwise the engine will convert the array to sparse storage, and put/get use a map to get from propertyName to value location.
I'm honestly not sure if any JS engine currently converts from sparse to flat storage after that conversion occurs.
Anyhoo, that's a fairly high level overview of what happens and leaves out a number of the more icky details, but that's the general implementation pattern. The specifics of how the additional storage, and how put/get are dispatched differs from engine to engine -- but this is the clearest i can really describe the design/implementation.
A minor addition point, while the ES spec refers to propertyName as a string JS engines tend to specialise on integer lookups as well, so someObject[someInteger] will not convert the integer to a string if you're looking at an object that has integer properties eg. Array, String, and DOM types (NodeLists, etc).

These are the result I get with your test
on Safari:
Array.push(n) 1,000,000 values: 0.124
sec
Array[n .. 0] = value
(descending) 1,000,000 values: 3.697
sec
Array[0 .. n] = value (ascending)
1,000,000 values: 0.073 sec
on FireFox:
Array.push(n) 1,000,000 values: 0.075 sec
Array[n .. 0] = value (descending) 1,000,000 values: 1.193 sec
Array[0 .. n] = value (ascending) 1,000,000 values: 0.055 sec
on IE7:
Array.push(n) 1,000,000 values: 2.828 sec
Array[n .. 0] = value (descending) 1,000,000 values: 1.141 sec
Array[0 .. n] = value (ascending) 1,000,000 values: 7.984 sec
According to your test the push method seems to be better on IE7 (huge difference), and since on the other browsers the difference is small, it seems to be the push method really the best way to add element to an array.
But I created another simple test script to check what method is fast to append values to an array, the results really surprised me, using Array.length seems to be much faster compared to using Array.push, so I really don't know what to say or think anymore, I'm clueless.
BTW: on my IE7 your script stops and browsers asks me if I want to let it go on (you know the typical IE message that says: "Stop runnign this script? ...")
I would recoomend to reduce a little the loops.

push() is a special case of the more general [[Put]] and therefore can be further optimized:
When calling [[Put]] on an array object, the argument has to be converted to an unsigned integer first because all property names - including array indices - are strings. Then it has to be compared to the length property of the array in order to determine whether or not the length has to be increased. When pushing, no such conversion or comparison has to take place: Just use the current length as array index and increase it.
Of course there are other things which will affect the runtime, eg calling push() should be slower than calling [[Put]] via [] because the prototype chain has to be checked for the former.
As olliej pointed out: actual ECMAScript implementations will optimize the conversion away, ie for numeric property names, no conversion from string to uint is done but just a simple type check. The basic assumption should still hold, though its impact will be less than I originally assumed.

array[n] = value, when previously initialised with a length (like new Array(n)), is faster than array.push, when ascending when n >= 90.
From inspecting the javascript source code of your page, your Array[0 .. n] = value (ascending) test does not initialize the array with a length in advance.
So Array.push(n) sometimes comes ahead on the first run, but on subsequent runs of your test then Array[0 .. n] = value (ascending) actually consistently performs best (in both Safari and Chrome).
If the code is modified so it initialises an array with a length in advance like var array = new Array(n) then Array[0 .. n] = value (ascending) shows that array[n] = value performs 4.5x to 9x faster than Array.push(n) in my rudimentary running of this specific test code.
This is consistent with other tests, like #Timo Kähkönen reported. See specifically this revision of the test he mentioned: https://jsperf.com/push-method-vs-setting-via-key/10
The modified code, so you may see how I edited it and initialised the array in a fair manner (not unnecessarily initialising it with a length for the array.push test case):
function testArr(n, doPush){
var now = new Date().getTime(),
duration,
report = ['<b>.push(n)</b>',
'<b>.splice(0,0,n)</b>',
'<b>.splice(n-1,0,n)</b>',
'<b>[0 .. n] = value</b> (ascending)',
'<b>[n .. 0] = value</b> (descending)'];
doPush = doPush || 5;
if (doPush === 1) {
var arr = [];
while (--n) {
arr.push(n);
}
} else if (doPush === 2) {
var arr = [];
while (--n) {
arr.splice(0,0,n);
}
} else if (doPush === 3) {
var arr = [];
while (--n) {
arr.splice(n-1,0,n);
}
} else if (doPush === 4) {
var arr = new Array(n);
for (var i = 0;i<n;i++) {
arr[i] = i;
}
} else {
while (--n) {
var arr = [];
arr[n] = n;
}
}
/*console.log(report[doPush-1] + '...'+ arr.length || 'nopes');*/
duration = ((new Date().getTime() - now)/1000);
$('zebradinges').innerHTML += '<br>Array'+report[doPush-1]+' 1.000.000 values: '+duration+' sec' ;
arr = null;
}

Push adds it to the end, while array[n] has to go through the array to find the right spot. Probably depends on browser and its way to handle arrays.

Related

function for manually order string

I'm trying to learn how functions work in JS.
This function should order the string, but the result is equal to the string himself.
what do I do wrong?
a = "awbc"
function f(str) {
let temporary
for (i = 0; i < a.length; i++) {
for (j = 0; j < a.length; j++) {
if (a[j] < a[j + 1]) {
temporary = a[j]
a[j] = a[j + 1]
a[j + 1] = temporary
}
}
}
return a
}
console.log(f(a))
You need to use a replace method on array values. See this for reference:
Replace string in javascript array
Strings are immutable
As already pointed out by Pointy (edit: believe it or not, no pun intended) in the comments, strings are immutable and cannot be changed. But what you can do is create one separate string for each character in your string and put that in an array using the split() method. Then you can sort that array and when it is sorted use join() to create another string consisting of those characters in sorted order.
Your bubbleSort() implementation
First of all the algorithm you are trying to implement is called bubble sort and is one of the easy but unfortunately slow sorting algorithms as it takes O(n²) in best, average and worst case runtime while good algorithms like merge sort only take O(n * log n).
Nevertheless, if you want to sort using bubbleSort() you need to make some changes to your code.
You are sorting in descending order therefore in every iteration of the outer loop the biggest value will be moved to the left-most position. Therefore in the next iteration you need to find the next biggest value and move it to the now left-most position. No need to check for all elements again as we already know that the left-most element is the biggest. Therefore start your inner loop at position i instead of 0. This will change nothing in the time complexity in big-O notation but will nevertheless improve the performance significantly.
Iteration i of outer loop
temporary
0
[ a,w,b,c ]
1
[ w,b,c,a ]
2
[ w,c,b,a ]
3
[ w,c,b,a ]
Also you are using a function in order to encapsulate functionality so you should not modify global variables within it. Use the str parameter you pass to it instead of a.
Last but not least you have this line temporary = a[j] but temporary should hold your array of individual character strings which you will destroy with this assignment. Create a new variable temp instead to do the swap.
Here an implementation of bubbleSort() with all those issues addressed.
/**
* Bubble sort algorithm which has sorts characters in a string in descending order.
* Best/ average and worst case runtime of bubble sort is O(n²).
* As we iterate n times over (n - i) items.
* T(n) = Sum{from 0 to n}[n * (n-i)] = (n + 1) * n = n² + n = O(n²)
* #param {string} str string
* #returns
*/
function bubbleSort(str) {
const temporary = str.split("");
// temporary now contains every single character
console.log("After split:", temporary);
// in each iteration of the outer loop the "biggest" letter will be sorted to the front
for (let i = 0; i < temporary.length; i++) {
// you need to start from i as otherwise you will be moving already sorted letters (as they are moved to the front)
for (let j = i; j < temporary.length - 1; j++) {
if (temporary[j] < temporary[j + 1]) {
// you need other variable here, otherwise you will override temporary
const temp = temporary[j];
temporary[j] = temporary[j + 1];
temporary[j + 1] = temp;
}
}
}
// now join characters back together to a string
console.log("After sorting: ", temporary);
return temporary.join("");
}
console.log(bubbleSort("awbc"));
console.log(bubbleSort("another _string with &)8 w?ird chars"));
.as-console-wrapper { max-height: 100% !important; top: 0; }

How to map 256 unique strings to the integers (0..255) with a memory-free function

Say I have strings like foo, bar, baz, hello, world, etc. up to 256 unique strings, so not very many. It could just as easily be 200 strings or 32 strings for all intents and purposes. Hopefully the solution could handle arbitrarily sized sets.
So you take that string and somehow map it to an integer 0-255. Without just doing this:
strings[currentString] = ID++
// strings['foo'] = 0
// strings['bar'] = 1
// strings['baz'] = 2
// ...
which would depend on the order they are inserted. Ideally they would be generated uniquely perhaps from a hash of the individual characters or bytes somehow, I'm not sure. But it would be a function without memory that takes an arbitrary string from a set of known size and maps it to an integer, so more like:
// strings['foo'] = 6 + 15 + 15 = 36
// strings['bar'] = 2 + 1 + 16 = 19
// ...
Although that wouldn't work because of collisions. I'm not sure how to go about designing a hash function like this. So somehow something else would work where there are never collisions to worry about.
function hash(string, size) {
// return unique integer within size
}
hash('foo', 256) // something like 123
hash('bar', 256) // something like 101
hash('foo', 100) // something else like 50
hash('bar', 100) // something else like 25
I would be interested to know too generally how to go about creating such a function, because it seems very difficult, but not strictly necessary for the question.
Also, looking to do this with basic JavaScript, not any special helper methods or browser-specific stuff.
The set of possible strings is known in advance.
I don't believe what you're looking for is possible unless you know what all 256 strings are ahead of time. Roughly, here's a proof of this:
Suppose there exists some f : S^* → [0, 255] (note: S^* means all finite length strings) s.t. for all 256-length subsets S ⊆ S^*, s_1, s_2 ∈ S, f(s_1) = f(s_2) <=> s_1 = s_2. Since f must not hold any memory of inputs it has seen, it must deterministically map strings to the same number in [0, 255], regardless of what subset this is in.
However, by the Pigeonhole Principle, since there are more than 256 strings, we must have at least two strings that map to the same value between [0, 255]. In particular, this means that if we take a subset S that contains both strings, the above property for f is violated, a contradiction. Thus, f cannot exist.
If you are allowed to know which 256 strings to hash, this is definitely possible. In general, what you're looking for is a perfect hash function.
This link provides an algorithm: https://www.cs.cmu.edu/~avrim/451f11/lectures/lect1004.pdf (refer to pages 56-57)
Quoting:
Method 1: an O(N^2)-space solution
Say we are willing to have a table whose size is quadratic in the size
N of our dictionary S. Then, here is an easy method for
constructing a perfect hash function. Let H be universal and
M=N^2. Then just pick a random h from H and try it out! The
claim is there is at least a 50% chance it will have no collisions.
Method 2: an O(N)-space solution
We will first hash into a table of size N using universal hashing.
This will produce some collisions (unless we are extraordinarily
lucky). However, we will then rehash each bin using Method 1, squaring
the size of the bin to get zero collisions. So, the way to think of
this scheme is that we have a first-level hash function h and
first-level table A, and then N second-level hash functions
h_1, ..., h_N and N second-level tables A_1, ..., A_N. To
lookup an element x, we first compute i=h(x) and then find the
element in A_i[h_i(x)].
Without just doing this: […] which would depend on the order they are inserted.
The set of possible strings is known in advance.
If you're fine with requiring the strings to be known upfront, but you just don't like the arbitrariness of using the order in which they happen to have been inserted, then one simple approach is to gather the strings into an array, sort that array (to get a deterministic ordering), and then use the resulting order:
var stringArray = [];
stringArray.push('foo');
stringArray.push('bar');
stringArray.push('baz');
// ...
stringArray = stringArray.sort();
var strings = {};
for (var i = 0; i < stringArray.length; ++i) {
strings[stringArray[i]] = i;
}
Here is a sketch of an idea that could have good results for a variety of inputs. The code below assumes lowercase english letters, no spaces, and only allows for up to 9 duplicates of any letter.
The idea is that any permutation of length n can be mapped to the integers modulo n by detecting how many times the permutation must be applied to itself before transforming into the identity permutation. Its "power" if you will. The catch is that any permutations with the same permutation cycles (the unordered integer partition that describes them), will result in the same "power", which we are using as the final hash.
To generate the permutation, each letter is assigned to one of nine buckets of 26, depending on if it's a duplicate, and pushed to an array, followed by the missing indexes from 0 to 255.
Like many hash functions, this can result in collisions (which could possibly be ameliorated through a few flags set in the function based on input analysis, although I have yet to consider that more carefully).
function seq(n){
return [...Array(n)].map((_,i) => i);
}
function permute(p1, p){
return p1.map(x => p[x]);
}
function areEqual(p1, p){
for (let i=0; i<p.length; i++)
if (p1[i] != p[i])
return false;
return true;
}
function findPower(p1){
let count = 0;
const p = seq(p1.length);
let p2 = p1.slice();
for (let i=0; i<p.length; i++){
if (!areEqual(p, p2)){
p2 = permute(p2, p1);
count++;
} else {
return count;
}
}
return count;
}
// Returns the permutation based on
// the string, s
function hash(s){
// Each letter is in one of
// 9 buckets of 26, depending
// on if it's a duplicate.
let fs = new Array(26).fill(0);
let result = [];
for (let i=0; i<s.length; i++){
let k = s.charCodeAt(i) - 97;
result.push(26 * fs[k] + k);
fs[k]++;
}
const set = new Set(result);
for (let i=0; i<256; i++)
if (!set.has(i))
result.push(i);
return result;
}
function h(s){
return findPower(hash(s));
}
var strings = [
'foo',
'bar',
'baz',
'hello',
'world',
'etc'];
for (let s of strings)
console.log(`${ s }: ${ h(s) }`);

Algorithm to merge multiple sorted sequences into one sorted sequence in javascript

I am looking for an algorithm to merge multiple sorted sequences, lets say X sorted sequences with n elements, into one sorted sequence in javascript , can you provide some examples?
note: I do not want to use any library.
Trying to solve https://icpc.kattis.com/problems/stacking
what will be the minimal number of operations needed to merge sorted arrays, under conditions :
Split: a single stack can be split into two stacks by lifting any top portion of the stack and putting it aside to form a new stack.
Join: two stacks can be joined by putting one on top of the other. This is allowed only if the bottom plate of the top stack is no larger than the top plate of the bottom stack, that is, the joined stack has to be properly ordered.
History
This problem has been solved for more than a century, going back to Hermann Hollerith and punchcards. Huge sets of punchcards, such as those resulting from a census, were sorted by dividing them into batches, sorting each batch, and then merging the sorted batches--the so-called
"merge sort". Those tape drives you see spinning in 1950's sci-fi movies were most likely merging multiple sorted tapes onto one.
Algorithm
All the algorithms you need can be found at https://en.wikipedia.org/wiki/Merge_algorithm. Writing this in JS is straightforward. More information is available in the question Algorithm for N-way merge. See also this question, which is an almost exact duplicate, although I'm not sure any of the answers are very good.
The naive concat-and-resort approach does not even qualify as an answer to the problem. The somewhat naive take-the-next-minimum-value-from-any-input approach is much better, but not optimal, because it takes more time than necessary to find the next input to take a value from. That is why the best solution using something called a "min-heap" or a "priority queue".
Simple JS solution
Here's a real simple version, which I make no claim to be optimized, other than in the sense of being able to see what it is doing:
const data = [[1, 3, 5], [2, 4]];
// Merge an array or pre-sorted arrays, based on the given sort criteria.
function merge(arrays, sortFunc) {
let result = [], next;
// Add an 'index' property to each array to keep track of where we are in it.
arrays.forEach(array => array.index = 0);
// Find the next array to pull from.
// Just sort the list of arrays by their current value and take the first one.
function findNext() {
return arrays.filter(array => array.index < array.length)
.sort((a, b) => sortFunc(a[a.index], b[b.index]))[0];
}
// This is the heart of the algorithm.
while (next = findNext()) result.push(next[next.index++]);
return result;
}
function arithAscending(a, b) { return a - b; }
console.log(merge(data, arithAscending));
The above code maintains an index property on each input array to remember where we are. The simplistic alternative would be to shift the element from the front of each array when it is its turn to be merged, but that would be rather inefficient.
Optimizing finding the next array to pull from
This naive implementation of findNext, to find the array to pull the next value from, simply sorts the list of inputs by the first element, and takes the first array in the result. You can optimize this by using a "min-heap" to manage the arrays in sorted order, which removes the need to resort them each time. A min-heap is a tree, consisting of nodes, where each node contains a value which is the minimum of all values below, with left and right nodes giving additional (greater) values, and so on. You can find information on a JS implementation of a min-heap here.
A generator solution
It might be slightly cleaner to write this as a generator which takes a list of iterables as inputs, which includes arrays.
// Test data.
const data = [[1, 3, 5], [2, 4]];
// Merge an array or pre-sorted arrays, based on the given sort criteria.
function* merge(iterables, sortFunc) {
let next;
// Create iterators, with "result" property to hold most recent result.
const iterators = iterables.map(iterable => {
const iterator = iterable[Symbol.iterator]();
iterator.result = iterator.next();
return iterator;
});
// Find the next iterator whose value to use.
function findNext() {
return iterators
.filter(iterator => !iterator.result.done)
.reduce((ret, cur) => !ret || cur.result.value < ret.result.value ? cur : ret,
null);
}
// This is the heart of the algorithm.
while (next = findNext()) {
yield next.result.value;
next.result = next.next();
}
}
function arithAscending(a, b) { return a - b; }
console.log(Array.from(merge(data, arithAscending)));
The naive approach is concatenating all the k sequences, and sort the result. But if each sequence has n elements, the the cost will be O(k*n*log(k*n)). Too much!
Instead, you can use a priority queue or heap. Like this:
var sorted = [];
var pq = new MinPriorityQueue(function(a, b) {
return a.number < b.number;
});
var indices = new Array(k).fill(0);
for (var i=0; i<k; ++i) if (sequences[i].length > 0) {
pq.insert({number: sequences[i][0], sequence: i});
}
while (!pq.empty()) {
var min = pq.findAndDeleteMin();
sorted.push(min.number);
++indices[min.sequence];
if (indices[min.sequence] < sequences[i].length) pq.insert({
number: sequences[i][indices[min.sequence]],
sequence: min.sequence
});
}
The priority queue only contains at most k elements simultaneously, one for each sequence. You keep extracting the minimum one, and inserting the following element in that sequence.
With this, the cost will be:
k*n insertions to a heap of k elements: O(k*n)
k*n deletions in a heap of k elements: O(k*n*log(k))
Various constant operations for each number: O(k*n)
So only O(k*n*log(k))
Just add them into one big array and sort it.
You could use a heap, add the first element of each sequence to it, pop the lowest one (that's your first merged element), add the next element from the sequence of the popped element and continue until all sequences are over.
It's much easier to just add them into one big array and sort it, though.
This is a simple javascript algo I came up with. Hope it helps. It will take any number of sorted arrays and do a merge. I am maintaining an array for index of positions of the arrays. It basically iterates through the index positions of each array and checks which one is the minimum. Based on that it picks up the min and inserts into the merged array. Thereafter it increments the position index for that particular array. I feel the time complexity can be improved. Will post back if I come up with a better algo, possibly using a min heap.
function merge() {
var mergedArr = [],pos = [], finished = 0;
for(var i=0; i<arguments.length; i++) {
pos[i] = 0;
}
while(finished != arguments.length) {
var min = null, selected;
for(var i=0; i<arguments.length; i++) {
if(pos[i] != arguments[i].length) {
if(min == null || min > arguments[i][pos[i]]) {
min = arguments[i][pos[i]];
selected = i;
}
}
}
mergedArr.push(arguments[selected][pos[selected]]);
pos[selected]++;
if(pos[selected] == arguments[selected].length) {
finished++;
}
}
return mergedArr;
}
This is a beautiful question. Unlike concatenating the arrays and applying a .sort(); a simple dynamical programming approach with .reduce() would yield a result in O(m.n) time complexity. Where m is the number of arrays and n is their average length.
We will handle the arrays one by one. First we will merge the first two arrays and then we will merge the result with the third array and so on.
function mergeSortedArrays(a){
return a.reduce(function(p,c){
var pc = 0,
cc = 0,
len = p.length < c.length ? p.length : c.length,
res = [];
while (p[pc] !== undefined && c[cc] !== undefined) p[pc] < c[cc] ? res.push(p[pc++])
: res.push(c[cc++]);
return p[pc] === undefined ? res.concat(c.slice(cc))
: res.concat(p.slice(pc));
});
}
var sortedArrays = Array(5).fill().map(_ => Array(~~(Math.random()*5)+5).fill().map(_ => ~~(Math.random()*20)).sort((a,b) => a-b));
sortedComposite = mergeSortedArrays(sortedArrays);
sortedArrays.forEach(a => console.log(JSON.stringify(a)));
console.log(JSON.stringify(sortedComposite));
OK as per #Mirko Vukušić's comparison of this algorithm with .concat() and .sort(), this algorithm is still the fastest solution with FF but not with Chrome. The Chrome .sort() is actually very fast and i can not make sure about it's time complexity. I just needed to tune it up a little for JS performance without touching the essence of the algorithm at all. So now it seems to be faster than FF's concat and sort.
function mergeSortedArrays(a){
return a.reduce(function(p,c){
var pc = 0,
pl =p.length,
cc = 0,
cl = c.length,
res = [];
while (pc < pl && cc < cl) p[pc] < c[cc] ? res.push(p[pc++])
: res.push(c[cc++]);
if (cc < cl) while (cc < cl) res.push(c[cc++]);
else while (pc < pl) res.push(p[pc++]);
return res;
});
}
function concatAndSort(a){
return a.reduce((p,c) => p.concat(c))
.sort((a,b) => a-b);
}
var sortedArrays = Array(5000).fill().map(_ => Array(~~(Math.random()*5)+5).fill().map(_ => ~~(Math.random()*20)).sort((a,b) => a-b));
console.time("merge");
mergeSorted = mergeSortedArrays(sortedArrays);
console.timeEnd("merge");
console.time("concat");
concatSorted = concatAndSort(sortedArrays);
console.timeEnd("concat");
5000 random sorted arrays of random lengths between 5-10.
es6 syntax:
function mergeAndSort(arrays) {
return [].concat(...arrays).sort()
}
function receives array of arrays to merge and sort.
*EDIT: as cought by #Redu, above code is incorrect. Default sort() if sorting function is not provided, is string Unicode. Fixed (and slower) code is:
function mergeAndSort(arrays) {
return [].concat(...arrays).sort((a,b)=>a-b)
}

Get first element in array with index not starting from 0

I'm using a javascript library which returns arrays not starting from zero like starting from 26 or 1500, what i want to do is a method to get the first element in that array regardless of the index number starting with 0 or any other number.
Are they any method to do this in javascript ?
I suggest to use Array#some. You get the first nonsparse element and the index. The iteration stops immediately if you return true in the callback:
var a = [, , 22, 33],
value,
index;
a.some(function (v, i) {
value = v;
index = i;
return true;
});
console.log(index, value);
The information below is generally useful, but for the problem the OP listed, Nina's answer is by far a better solution.
Those are called sparse arrays and they're one of the few situations where you may want to use for-in on an array.
Remember that arrays are objects in JavaScript, and array entries are properties keyed by names (array indexes) that meet certain criteria. So we can use the features that let us discover the properties on an object to find the indexes on your sparse array.
for-in example:
for (var n in theArray) {
if (theArray.hasOwnProperty(n) && isArrayIndex(n)) {
// Use theArray[n]
}
}
This answer shows how you can determine that n is an array index as opposed to being some other property. A very technical definition would be
function isArrayIndex(n) {
return /^0$|^[1-9]\d*$/.test(n) &&
n <= 4294967294;
}
...but a definition that's good enough for most of us would be
function isArrayIndex(n) {
return !isNaN(parseInt(n, 10));
}
Similarly, you can use Object.keys; since it only looks at own enumerable properties, you don't need the hasOwnProperty check:
Object.keys(theArray).forEach(function(n) {
if (isArrayIndex(n)) {
// ...
}
});
Note that officially, neither of those is in any particular order, not even in ES2015 ("ES6"). So in theory, you could see the indexes out of numeric order. In the real world, I've never seen an even vaguely-modern JavaScript engine that returned array indexes out of order. They're not required to, but every one I've tried does.
So officially, you would need to get a full list and then find the minimum value in it:
var min = Object.keys(theArray).reduce(function(min, n) {
var i = parseInt(n, 10);
return isNaN(i) || (min !== undefined && min > i) ? min : i;
}, undefined);
That'll given you undefined if the array is empty, or the min index if it isn't. But if you want to make the assumption you'll get the keys in numeric order:
// Makes an assumption that may not be true
var min = +Object.keys(theArray).filter(isArrayIndex)[0];
If you're using a JavaScript engine that's entirely up-to-date, you can rely on the order returned by Object.getOwnPropertyNames, which is required to list the array indexes in order.
var min = +Object.getOwnPropertyNames(theArray).filter(isArrayIndex)[0];
It may be useful to use a filter function on the array to get back a normalised array.
var fullArray = array.filter(function(n){
return n != undefined;
});
fullArray[0]
The answers here may help you decide Remove empty elements from an array in Javascript
I guess one alternative to Array.prototype.some() is the Array.prototype.findIndex() method. These are much faster than filter alone and will keep your array and indices untouched.
var arr = new Array(1000),
fi = -1;
arr[777] = 1453; // now we have a nice sparse array
fi = arr.findIndex(f => f !== void 0); // void 0 is the perfect undefined
console.log(fi);
console.log(arr[fi]);
With this piece of code you can find first assigned value index and then get the value from your array:
var a = [, , 22, 33];
var value = a.find((v, i) => i in a);
console.log(value);
/* ---------------------------------------------- */
var i = 0
while (!(i in a) && i < a.length) i++; // If i === a.length then the array is emtpy
console.info(i, a[i]);
First implementation uses Array.prototype.find which makes less variable usage so this is cleaner but to find the index you should call indexOf over the array.
But the second one is a little bit old fashioned but give the chance of having index without extra efforts.
BTW Nina's seems better. (can make it shorter?)
const arr = [0,1,2]
// using destructuring to get the first element
let [first] = arr
// plus: using destructuring to get the last element
let [first] = [...arr].reverse()

Array slow elements removal

I have array and I want to remove N elements from its head.
Lets say, array (with floats) has 1M elements and I want first 500K to go out. I have two ways, call shift 500K times in loop or call splice(0,500000).
The thing is, first solution is horrible idea (it's very very slow). Second is slow too because splice returns removed part from array in a new array (well, it just allocate 500K floats and throw them out of window).
In my app, I'm doing some things with really big matrices, and unfortunately, elements removal via splice is slow for me. Is there some faster way how to achieve it?
I would expect that Array#slice would be at least as fast as either of those options and probably faster. It does mean temporarily allocating duplicated memory, but 1M numbers is only about 64MB of memory (assuming the JavaScript engine has been able to use a true array under the covers), so temporarily having the original 64MB plus the 32MB for the ones you want to keep before releasing the original 64MB seems fairly cheap:
array = array.slice(500000);
This also has the advantage that it won't force the JavaScript engine into using an object rather than an array under the covers. (Other things you're doing may cause that, but...)
You've said you're doing this with floats, you might look at using Float64Array rather than untyped arrays. That limits the operations you can perform, but ensures that you don't end up with unoptimized arrays. When you delete entries from arrays, you can end up with unoptimized arrays with markedly slower access times than optimized arrays, as they end up being objects with named properties rather than offset accesses. (A good JavaScript engine will keep them optimized if it can; using typed arrays would help prevent you from blowing its optimizations.)
This (dashed off and quite certainly flawed) NodeJS test suggests that splice is anywhere from 60% to 95% slower than slice, and that V8 does a great job keeping the array optimized as the result for the typed array is virtually identical to the result for the untyped array in the slice case:
"use strict";
let sliceStats = createStats();
let sliceTypedStats = createStats();
let spliceStats = createStats();
for (let c = 0; c < 100; ++c) {
if (test(buildUntyped, sliceStats, testSlice).length != 500000) throw new Error("1");
if (test(buildTyped, sliceTypedStats, testSlice).length != 500000) throw new Error("2");
if (test(buildUntyped, spliceStats, testSplice).length != 500000) throw new Error("3");
console.log(c);
}
console.log("slice ", avg(sliceStats.sum, sliceStats.count));
console.log("sliceTyped", avg(sliceTypedStats.sum, sliceTypedStats.count));
console.log("splice ", avg(spliceStats.sum, spliceStats.count));
function avg(sum, count) {
return (sum / count).toFixed(3);
}
function createStats() {
return {
count: 0,
sum: 0
};
}
function buildUntyped() {
let a = [];
for (let n = 0; n < 1000000; ++n) {
a[n] = Math.random();
}
return a;
}
function buildTyped() {
let a = new Float64Array(1000000);
for (let n = 0; n < 1000000; ++n) {
a[n] = Math.random();
}
return a;
}
function test(build, stats, f) {
let a;
let ignore = 0;
let start = Date.now();
for (let i = 0; i < 10; ++i) {
let s = Date.now();
a = build();
ignore += Date.now() - s;
a = f(a);
}
stats.sum += Date.now() - start - ignore;
++stats.count;
return a;
}
function testSlice(a) {
return a.slice(500000);
}
function testSplice(a) {
a.splice(0, 500000);
return a;
}
Immutable.js solves this problem by structural sharing. It does not copy the entries as splice would do but returns a reference on the included
parts of the array. You would need to move your array to the Immutable.js data structure and then call the immutable operation splice.

Categories

Resources