Related
I have to improve my function because it is being Terminated due to timeout when the arr or the queries is too big. It is a simple function to do some operations in a array pre-defined with a length n, and the queries defines the operations to do into array (arr).
Each line of a query means the range (queries[i][0] is the beginning and queries[i][1] is the final range from arr[]). And the value to sum the arr (queries[i][2]). So, if the queries have three rows, we have three operations to do. This function returns the max element from this arr.
Is there something to improve this code?
I think that Array(n).fill(0) and Math.max.apply(Math, arr) can be the problem.
Otherwise, in while loop, is there some way to do the verification and operation once rather than walking through the array and doing verification at each element? Thanks
function arrayManipulation(n, queries) {
var a;
var b;
var k;
var i = 0;
var arr = Array(n).fill(0);
while (queries[i] != undefined) {
a = (queries[i][0] - 1);
b = (queries[i][1] - 1);
k = queries[i][2];
for (a; a <= b; a++) {
arr[a] = (arr[a] + k)
}i++
}
k = Math.max.apply(Math, arr);
return k
}
Every iteration of the loop, you repeat the same lookup into the array, queries[i], four times:
while (queries[i] != undefined) {
a = (queries[i][0] - 1);
b = (queries[i][1] - 1);
k = queries[i][2];
//...
Do the same with a for loop:
var query;
for (var i = 0, len = queries.length; i < len; i++) {
query = queries[i];
a = query[0] - 1;
b = query[1] - 1;
k = query[2];
// ...the rest of the loop
}
I have three sorted arrays like below
[{name:"a"}, {name:"b"}, {name:"m"}, {name:"x"}]
[{name:"a"}, {name:"e"}, {name:"i"}, {name:"o"}]
[{name:"g"}, {name:"h"}, {name:"m"}, {name:"n"}]
Those arrays are sorted based on name property of each object in Array. Here is the method I converted from Java to merge two sorted arrays
function mergeSorted(a, b) {
var answer = new Array(a.length + b.length), i = 0, j = 0, k = 0;
while (i < a.length && j < b.length) {
if (a[i].name < b[j].name) {
answer[k] = a[i];
i++;
}else {
answer[k] = b[j];
j++;
}
k++;
}
while (i < a.length) {
answer[k] = a[i];
i++;
k++;
}
while (j < b.length) {
answer[k] = b[j];
j++;
k++;
}
return answer;
}
Here is the working fiddle with two arrays http://jsfiddle.net/euRn5/. What is the best approach to achieve the same with n number of Arrays, the thought I have in my mind currently is take one by one, merge it with previously merged till the last item, like n += i stuff. Is this a best approach?
The standard and most understanding code I believe..
function mergeArray(arr1, arr2) {
var new_array = [];
var i = 0,
j = 0,
index = 0;
while (new_array.length != (arr1.length + arr2.length) - 1) {
if (arr1[i] < arr2[j]) {
new_array.push(arr1[i]);
i++;
} else {
new_array.push(arr2[j]);
j++;
}
}
return new_array;
}
Function call:
var merged_array = mergeArray([1,6,9,95], [2,7,10,11,14,18]);
Update:
Seeing as it is current_year this would now be:
const mergeAll = (...arrays) => arrays.reduce(mergeSorted);
Original:
If you're feeling functional this is a perfect place to use reduce.
var mergeAll = function(){
return Array.prototype.slice.call(arguments).reduce(mergeSorted);
};
example:
var a = [{name:"a"}, {name:"b"}, {name:"m"}, {name:"x"}];
var b = [{name:"a"}, {name:"e"}, {name:"i"}, {name:"o"}];
var c = [{name:"g"}, {name:"h"}, {name:"m"}, {name:"n"}];
console.log(mergeAll(a,b,c).map(function(x){return x.name;}));
jsfiddle: http://jsfiddle.net/FeT6m/
The native implementations are not always the fastest (as you may have noticed) and have, historically, been somewhat sluggish, due to extensive error checking. That being said, there may be performance enhancements in the future, due to more robust integration with the hardware or routines specifically built to optimize certain tasks. If you write your own code, your application won't be able to take advantage of these boosts in performance once they're implemented. It's up to you to decide where the advantages lie and what the risks are.
At any rate, I've written a prettier version of your optimized code for funsies:
function mergeSorted(a,b){
var alen = a.length
, blen = b.length
, i, j, k = j = i = 0
, answer = new Array(alen + blen)
;//var
while(i < alen && j < blen)
answer[k++] = a[i].name < b[j].name ? a[i++] : b[j++];
while(i < alen) answer[k++] = a[i++];
while(j < blen) answer[k++] = b[j++];
return answer;
}
Faster, merges in only 1 pass, with more flexibility (keepDuplicates, custom comparator):
/* mergeSortedArrays(arrays[, keepDuplicates[, comparator[, thisArg]]])
Merges multiple sorted arrays into a new sorted array.
Arguments:
- arrays: array of sorted arrays to be merged
- keepDuplicates (optional): (true/false) whether to keep duplicate values
Default: false
- comparator (optional): function used to compare values
Default: sort numbers in ascending order
Example comparator: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
- thisArg (optional): comparator is bound to thisArg when invoked
Returns: a new sorted array containing all the values from the arrays
*/
function mergeSortedArrays(arrays, keepDuplicates, comparator, thisArg) {
// Coerce to boolean to speed up testings in some javascript engines:
keepDuplicates = !!keepDuplicates;
// By default, sort numbers in ascending order:
if(!comparator) comparator = function(a, b) { return a - b; };
var nb = arrays.length, // Number of arrays to be merged
iter = new Array(nb), // Current position of iteration of each array
next = [], // Keep each array sorted by the value of their next element
length = 0; // The combined length of all arrays
// Populate iter and next:
for(var i = 0, arr; i < nb; i++) {
arr = arrays[i];
iter[i] = 0;
if(arr.length > 0) {
insertNextIndex(next, i, arr[0], comparator, thisArg);
}
length += arr.length;
}
// Insert index of array into next:
function insertNextIndex(next, index, val, comparator, thisArg) {
var i = next.length;
while(i--) { // Reverse loop...
var j = next[i];
if(comparator.call(thisArg, arrays[j][iter[j]], val) >= 0) { // ...until we find a greater value
break;
}
}
next.splice(i + 1, 0, index);
}
var merged = keepDuplicates ? new Array(length) : [],
k = 0, // Iterate over merged
min, val, lastVal;
// First iteration to get a value for lastVal (for duplicate checks):
if(!keepDuplicates && next.length > 0) {
min = next.pop();
arr = arrays[min];
i = iter[min]++;
val = arr[i];
merged[k++] = val;
lastVal = val;
if(++i < arr.length) { // If available, insert next value in next:
insertNextIndex(next, min, arr[i], comparator, thisArg);
}
}
// Merge multiple arrays:
while(next.length > 1) { // While there is still multiple arrays to be merged
min = next.pop();
arr = arrays[min];
i = iter[min]++;
val = arr[i];
if(keepDuplicates || comparator.call(thisArg, lastVal, val) !== 0) {
merged[k++] = val;
lastVal = val;
}
if(++i < arr.length) { // If available, insert next value in next:
insertNextIndex(next, min, arr[i], comparator, thisArg);
}
}
// When there remain only 1 array with unmerged values, use a faster loop:
if(next.length > 0) {
arr = arrays[next[0]];
i = iter[next[0]];
length = arr.length;
while(i < length) { // To the end
val = arr[i++];
if(keepDuplicates || comparator.call(thisArg, lastVal, val) !== 0) {
merged[k++] = val;
lastVal = val;
}
}
}
return merged;
}
Merging in 1 pass eliminates the creation of intermediate arrays which takes time and memory. Also, the number of comparisons is nicely reduced by keeping a sorted list of the next element from each array (see the next array). And when array sizes are known, they are pre-allocated to prevent dynamic re-allocations (though that will depend on your javascript engine).
For your case, I would call it like this:
mergeSortedArrays(arrays, true, function(a, b) {
return a.name < b.name ? -1 : 1;
});
Note: If you have a large number of arrays you may benefit from using a binary search instead of the linear search in insertNextIndex(). Or from using a Binary Heap for next.
Edited to reflect that Exception's original solution, extended by calling it like mergeSorted(mergeSorted(a,b),c) is faster than my solution here.
Javascript's builtin sort is [not] fast enough that you can just concatenate all the arrays together and sort the entire thing in one go. Javascript is not good for re-implementing things that should be done lower level.
var a1 = [{name:"a"}, {name:"b"}, {name:"m"}, {name:"x"}]
var a2 = [{name:"a"}, {name:"e"}, {name:"i"}, {name:"o"}]
var a3 = [{name:"g"}, {name:"h"}, {name:"m"}, {name:"n"}]
a1.concat(a2,a3).sort(function(a,b){return (a.name>b.name)-(a.name<b.name)})
// [{name:"a"}, {name:"a"}, {name:"b"}, {name:"e"}, {name:"h"}, {name:"i"}, {name:"g"}, {name:"m"}, {name:"m"}, {name:"n"}, {name:"o"}, {name:"x"}]
I like thetheChad's answer but I prefer something more readable
let mergeSorted = function(a, b) {
let alen = a.length;
let blen = b.length;
let i = j = k = 0;
let sortedNums = new Array(alen + blen);
while (i < alen && j < blen) {
if (a[i] < b[j]) {
sortedNums[k++] = a[i++];
} else {
sortedNums[k++] = b[j++];
}
}
while (i < alen) {
sortedNums[k++] = a[i++];
}
while (j < blen) {
sortedNums[k++] = b[j++];
}
return sortedNums;
};
basically mergeSorted allocates memory for a new array the size of the two input arrays combined. Then it populates the destination with items in order. If one of the arrays is longer we while loop until all the items are copied over. Should pass the following test suite.
// test
console.log(mergeSorted([1,6,9,95], [2,7,10,11,14,18]));
console.log(mergeSorted([2,3], [1]))
console.log(mergeSorted([0,0], [0,0]))
console.log(mergeSorted([1,3], [2]))
console.log(mergeSorted([1,2], [3,4]))
I have three sorted arrays like below
[{name:"a"}, {name:"b"}, {name:"m"}, {name:"x"}]
[{name:"a"}, {name:"e"}, {name:"i"}, {name:"o"}]
[{name:"g"}, {name:"h"}, {name:"m"}, {name:"n"}]
Those arrays are sorted based on name property of each object in Array. Here is the method I converted from Java to merge two sorted arrays
function mergeSorted(a, b) {
var answer = new Array(a.length + b.length), i = 0, j = 0, k = 0;
while (i < a.length && j < b.length) {
if (a[i].name < b[j].name) {
answer[k] = a[i];
i++;
}else {
answer[k] = b[j];
j++;
}
k++;
}
while (i < a.length) {
answer[k] = a[i];
i++;
k++;
}
while (j < b.length) {
answer[k] = b[j];
j++;
k++;
}
return answer;
}
Here is the working fiddle with two arrays http://jsfiddle.net/euRn5/. What is the best approach to achieve the same with n number of Arrays, the thought I have in my mind currently is take one by one, merge it with previously merged till the last item, like n += i stuff. Is this a best approach?
The standard and most understanding code I believe..
function mergeArray(arr1, arr2) {
var new_array = [];
var i = 0,
j = 0,
index = 0;
while (new_array.length != (arr1.length + arr2.length) - 1) {
if (arr1[i] < arr2[j]) {
new_array.push(arr1[i]);
i++;
} else {
new_array.push(arr2[j]);
j++;
}
}
return new_array;
}
Function call:
var merged_array = mergeArray([1,6,9,95], [2,7,10,11,14,18]);
Update:
Seeing as it is current_year this would now be:
const mergeAll = (...arrays) => arrays.reduce(mergeSorted);
Original:
If you're feeling functional this is a perfect place to use reduce.
var mergeAll = function(){
return Array.prototype.slice.call(arguments).reduce(mergeSorted);
};
example:
var a = [{name:"a"}, {name:"b"}, {name:"m"}, {name:"x"}];
var b = [{name:"a"}, {name:"e"}, {name:"i"}, {name:"o"}];
var c = [{name:"g"}, {name:"h"}, {name:"m"}, {name:"n"}];
console.log(mergeAll(a,b,c).map(function(x){return x.name;}));
jsfiddle: http://jsfiddle.net/FeT6m/
The native implementations are not always the fastest (as you may have noticed) and have, historically, been somewhat sluggish, due to extensive error checking. That being said, there may be performance enhancements in the future, due to more robust integration with the hardware or routines specifically built to optimize certain tasks. If you write your own code, your application won't be able to take advantage of these boosts in performance once they're implemented. It's up to you to decide where the advantages lie and what the risks are.
At any rate, I've written a prettier version of your optimized code for funsies:
function mergeSorted(a,b){
var alen = a.length
, blen = b.length
, i, j, k = j = i = 0
, answer = new Array(alen + blen)
;//var
while(i < alen && j < blen)
answer[k++] = a[i].name < b[j].name ? a[i++] : b[j++];
while(i < alen) answer[k++] = a[i++];
while(j < blen) answer[k++] = b[j++];
return answer;
}
Faster, merges in only 1 pass, with more flexibility (keepDuplicates, custom comparator):
/* mergeSortedArrays(arrays[, keepDuplicates[, comparator[, thisArg]]])
Merges multiple sorted arrays into a new sorted array.
Arguments:
- arrays: array of sorted arrays to be merged
- keepDuplicates (optional): (true/false) whether to keep duplicate values
Default: false
- comparator (optional): function used to compare values
Default: sort numbers in ascending order
Example comparator: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
- thisArg (optional): comparator is bound to thisArg when invoked
Returns: a new sorted array containing all the values from the arrays
*/
function mergeSortedArrays(arrays, keepDuplicates, comparator, thisArg) {
// Coerce to boolean to speed up testings in some javascript engines:
keepDuplicates = !!keepDuplicates;
// By default, sort numbers in ascending order:
if(!comparator) comparator = function(a, b) { return a - b; };
var nb = arrays.length, // Number of arrays to be merged
iter = new Array(nb), // Current position of iteration of each array
next = [], // Keep each array sorted by the value of their next element
length = 0; // The combined length of all arrays
// Populate iter and next:
for(var i = 0, arr; i < nb; i++) {
arr = arrays[i];
iter[i] = 0;
if(arr.length > 0) {
insertNextIndex(next, i, arr[0], comparator, thisArg);
}
length += arr.length;
}
// Insert index of array into next:
function insertNextIndex(next, index, val, comparator, thisArg) {
var i = next.length;
while(i--) { // Reverse loop...
var j = next[i];
if(comparator.call(thisArg, arrays[j][iter[j]], val) >= 0) { // ...until we find a greater value
break;
}
}
next.splice(i + 1, 0, index);
}
var merged = keepDuplicates ? new Array(length) : [],
k = 0, // Iterate over merged
min, val, lastVal;
// First iteration to get a value for lastVal (for duplicate checks):
if(!keepDuplicates && next.length > 0) {
min = next.pop();
arr = arrays[min];
i = iter[min]++;
val = arr[i];
merged[k++] = val;
lastVal = val;
if(++i < arr.length) { // If available, insert next value in next:
insertNextIndex(next, min, arr[i], comparator, thisArg);
}
}
// Merge multiple arrays:
while(next.length > 1) { // While there is still multiple arrays to be merged
min = next.pop();
arr = arrays[min];
i = iter[min]++;
val = arr[i];
if(keepDuplicates || comparator.call(thisArg, lastVal, val) !== 0) {
merged[k++] = val;
lastVal = val;
}
if(++i < arr.length) { // If available, insert next value in next:
insertNextIndex(next, min, arr[i], comparator, thisArg);
}
}
// When there remain only 1 array with unmerged values, use a faster loop:
if(next.length > 0) {
arr = arrays[next[0]];
i = iter[next[0]];
length = arr.length;
while(i < length) { // To the end
val = arr[i++];
if(keepDuplicates || comparator.call(thisArg, lastVal, val) !== 0) {
merged[k++] = val;
lastVal = val;
}
}
}
return merged;
}
Merging in 1 pass eliminates the creation of intermediate arrays which takes time and memory. Also, the number of comparisons is nicely reduced by keeping a sorted list of the next element from each array (see the next array). And when array sizes are known, they are pre-allocated to prevent dynamic re-allocations (though that will depend on your javascript engine).
For your case, I would call it like this:
mergeSortedArrays(arrays, true, function(a, b) {
return a.name < b.name ? -1 : 1;
});
Note: If you have a large number of arrays you may benefit from using a binary search instead of the linear search in insertNextIndex(). Or from using a Binary Heap for next.
Edited to reflect that Exception's original solution, extended by calling it like mergeSorted(mergeSorted(a,b),c) is faster than my solution here.
Javascript's builtin sort is [not] fast enough that you can just concatenate all the arrays together and sort the entire thing in one go. Javascript is not good for re-implementing things that should be done lower level.
var a1 = [{name:"a"}, {name:"b"}, {name:"m"}, {name:"x"}]
var a2 = [{name:"a"}, {name:"e"}, {name:"i"}, {name:"o"}]
var a3 = [{name:"g"}, {name:"h"}, {name:"m"}, {name:"n"}]
a1.concat(a2,a3).sort(function(a,b){return (a.name>b.name)-(a.name<b.name)})
// [{name:"a"}, {name:"a"}, {name:"b"}, {name:"e"}, {name:"h"}, {name:"i"}, {name:"g"}, {name:"m"}, {name:"m"}, {name:"n"}, {name:"o"}, {name:"x"}]
I like thetheChad's answer but I prefer something more readable
let mergeSorted = function(a, b) {
let alen = a.length;
let blen = b.length;
let i = j = k = 0;
let sortedNums = new Array(alen + blen);
while (i < alen && j < blen) {
if (a[i] < b[j]) {
sortedNums[k++] = a[i++];
} else {
sortedNums[k++] = b[j++];
}
}
while (i < alen) {
sortedNums[k++] = a[i++];
}
while (j < blen) {
sortedNums[k++] = b[j++];
}
return sortedNums;
};
basically mergeSorted allocates memory for a new array the size of the two input arrays combined. Then it populates the destination with items in order. If one of the arrays is longer we while loop until all the items are copied over. Should pass the following test suite.
// test
console.log(mergeSorted([1,6,9,95], [2,7,10,11,14,18]));
console.log(mergeSorted([2,3], [1]))
console.log(mergeSorted([0,0], [0,0]))
console.log(mergeSorted([1,3], [2]))
console.log(mergeSorted([1,2], [3,4]))
Guys I need your opinion; I've encountered this earlier during my interview, I just want to confirm I understood the question right and I got the answer correctly. Thank you. Please check the question and my answer below:
Take an input single dimensional array [1,2,3,4] and output the product of the integers excluding the current index [24,12,8,6];
//My answer
function calculate(values:Array):Array {
var resultArray:Array = new Array();
for(var i:int = 0; i < values.length; i++) {
var getVal1:Number = 1;
for(var k:int = 0; k <= values.length; k++) {
if(i != k) {
var getVal2:Number = values[k];
getVal1 *= getVal2;
}
}
resultArray.push(getVal1);
}
return resultArray;
}
Nested loops seems like a very messy way to go.
Assuming relatively up-to-date browser (IE 8 and below are out) or suitable shim:
var resultArray = sourceArray.map(function(val,ind,arr) {
arr = arr.slice(0); // create copy of array to work on here
arr.splice(ind,1); // remove current item from array
return arr.reduce(function(prev,curr) {return prev*curr;},1);
});
Array.prototype.map
Array.prototype.reduce
EDIT Here's another way that should be more efficient:
var product = sourceArray.reduce(function(prev,curr) {return prev*curr;},1);
var resultArray = sourceArray.map(function(val) {return product/val;});
Your solution gives the correct answer, but there is a much more efficient method to calculate the new array:
function calculate(values:Array):Array {
var resultArray:Array = new Array();
var product:int = 1;
for(var i:int = 0; i < values.length; i++) {
product *= values[i];
}
for(var i:int = 0; i < values.length; i++) {
resultArray.push(product / values[i]);
}
return resultArray;
}
This solution has O(n) execution time, while your code has O(n²) execution time.
That should work. You can do it easier and more efficiently by multiplying all items first:
function calculate(values) {
var prod = 1;
for (var i = 0; i < values.length; i++) prod *= values[i];
var result = [];
for (i = 0; i < values.length; i++) result.push(prod / values[i]);
return result;
}
I believe that my code below is very easy to read. And has no nested loops, but two consecutives. My answer would be:
function calculate(array){
var total = array.reduce(function(a, b){
return a * b;
});
return array.map(function(element){
return total / element;
});
}
Though I like #Kolink's short-and-efficient solution best, here's another way to solve the task - not using division but still being in O(n):
function calculate(values) {
var acc = 1,
l = values.length,
result = new Array(l);
for (var i=0; i<l; i++) {
result[i] = acc;
acc *= values[i];
}
acc = 1;
while(i--) {
result[i] *= acc;
acc *= values[i]
}
return result;
}
Or, the same thing but a little obfuscated*:
function calculate(values) {
var acc = 1,
i = 0,
l = values.length,
result = new Array(l);
if (l)
result[i] = 1;
while( ++i < l)
result[i] = acc *= values[i-1];
i -= acc = 1;
while (i--)
result[i] *= acc *= values[i+1];
return result;
}
*: I like shorthand operators!
How would I go about calculating the average of all the values in a multidimensional array? I've written a function to calculate the average from a 1-dimensional array, but I'm not sure what the best method is when there are more than 1-dimensions.
For example, let's say we have the following:
var A = Array(3);
for (i=0; i<A.length; i++) {
A[i] = new Array(2);
for (j=0; j<A[i].length; j++) {
A[i][j] = i+j;
}
}
Therefore, A is a 2-dimensional array, or 3x2 matrix:
A = 0 1
1 2
2 3
So I'd like to find the average of all the values, which in this case would equal 1.5. I imagine I need to create a new 1-dimensional array of all the values, which I could then feed into my averaging function. However, I'm not sure of the easiest way to do this when the array is highly-dimensional (e.g. 5x3x6x9).
Thanks!
EDIT
Thanks everyone! I've used your advice and flattened the array using code I found in one of the attached links which uses the reduce function. My averaging function is now like this:
function average(x) {
// Flatten multi-dimensional array
while (x[0] instanceof Array) {
x = x.reduce( function(a, b) { return a.concat(b); } );
}
// Calculate average
return x.reduce( function(a, b) { return a + b; } )/x.length;
}
You can use this code to flatten the multi-dimensional array:
function flatten(array){
var flat = [];
for (var i = 0, l = array.length; i < l; i++){
var type = Object.prototype.toString.call(array[i]).split(' ').pop().split(']').shift().toLowerCase();
if (type) { flat = flat.concat(/^(array|collection|arguments|object)$/.test(type) ? flatten(array[i]) : array[i]); }
}
return flat;
}
and then just sum and divide:
var total = 0;
for (var i = 0, l = flattenedArray.length; i<l; i++) {
total += flattenedArray[i];
}
var average = total/flattenedArray.length;
I don't see any particular reason you need to create a new array. Just loop through the ones you have:
var i, j, sub, total, count, avg;
total = count = 0;
for (i = 0; i < A.length; ++i) {
sub = A[i];
count += sub.length;
for (j = 0; j < sub.length; ++j) {
total += sub[j];
}
}
avg = count === 0 ? NaN : total / count;
Note that the above assumes the arrays are not sparse (they aren't in your example).
// The simplest method is to flatten the array
Array.prototype.flatten= function(){
var A= [];
this.forEach(function(itm){
if(!itm || !itm.flatten)A.push(itm);
else{
A= A.concat(itm.flatten());
}
});
return A;
}
// shim for older browsers (without array forEach)
Array.prototype.forEach= [].forEach || function(fun, scope){
var T= this, L= T.length, i= 0;
if(typeof fun== 'function'){
while(i< L){
if(i in T){
fun.call(scope, T[i], i, T);
}
++i;
}
}
return T;
}
var a=[[1,2,3,[1,2,3]],[4,5,6,[7,8,[1,2,3,[1,2]]]],11,[1,[2,[2,4,[5]]]]];
a.flatten().join('\n')
/* returned value: (String)
1
2
3
1
2
3
4
5
6
7
8
1
2
3
1
2
11
1
2
2
4
5
*/
You can flatten your multidimensional-array with this function:
function flatten(arr) {
var acc = [];
var f = function(arr) {
for (var i = 0; i < arr.length; ++i) {
if (arr[i] instanceof Array) {
f(arr[i]);
}
else {
acc.push(arr[i]);
}
}
};
f(arr);
return acc;
}
And then you can calculate the average with your function.