Practicing callbacks & higher ordered function & found this question online.
var merge = function(array1, array2, callback){
//your code here.
}
var x = merge([1, 2, 3, 4], [5, 6, 7, 8], function(a, b){
return a + b;
});
//x should now equal [6, 8, 10, 12].
Here's my take on this problem.
var merge = function(array1, array2, callback){
for(var i = 0; i < array1.length; i++) {
callback(array1[i], array2[i]);
}
}
var x = merge([1, 2, 3, 4], [5, 6, 7, 8], function(a, b){
return a + b;
});
When I console.log(x), the console returns "undefined" so I'm guessing it has to do w/ the value of x not being an array. I can see that the math is being done correctly though, for when I change "return a + b" to "console.log(a + b)" I get the right numbers but just not in array form. Can anyone point me towards the right direction?
You are calling the callback, but you are ignoring the value returned by it. You should accumulate all the values in an array and your should return the array from merge.
For example,
function merge(array1, array2, callback) {
// Define an array object to accumulate the results from `callback`
var result = [];
for (var i = 0; i < array1.length; i++) {
// Accumulate the result of `callback` in `result` array
result.push(callback(array1[i], array2[i]));
}
// Return the `result` array
return result;
}
Note: If the arrays are of different sizes then running the loop based on array1's length will not be correct always. So, you might want to either
go with the smallest length of two arrays and ignore elements from the longer array
or use a default value for the elements of the shorter array.
If you choose go with the first method, then you just need to adjust the loop condition, like this
var minLen = Math.min(array1.length, array2.length);
for (var i = 0; i < minLen; i++) {
...
If you choose to go with the second method, then you need to run till the maximum of two arrays and use default values, like this
var maxLen = Math.max(array1.length, array2.length);
for (var i = 0; i < maxLen; i++) {
result.push(callback(array1[i] || 0, array2[i] || 0));
}
Here, if the value of array1[i] returns undefined (if the index is not found in an array, undefined will be returned), it means that array1 is shorter than array2, so the default value 0 will be used.
Related
So, I was working on this challenge to return the third largest number in an array. I had got it worked out until I realized that I must account for repeat numbers. I handled this by adding 3 layers of for loops with variables i, j, and k. You'll see what I mean in the code. This is not terribly efficient or scalable.
My question is, how can I optimize this code? What other methods should I be using?
function thirdGreatest (arr) {
arr.sort(function(a, b) {
if (a < b) {
return 1;
} else if (a > b) {
return -1;
} else {
return 0;
}
});
for ( var i = 0; i < arr.length; i++) {
for (var j = 1; j < arr.length; j++) {
for (var k = 2; k < arr.length; k++) {
if (arr[i] > arr[j]) {
if (arr[j] > arr[k]) {
return arr[k];
}
}
}
}
}
}
console.log(thirdGreatest([5, 3, 23, 7,3,2,5,10,24,2,31, 31, 31])); // 23
console.log(thirdGreatest([5, 3, 23, 7,3,2,5,10,24,2,31])) // 23
console.log(thirdGreatest([5, 3, 7, 4])); // 4
console.log(thirdGreatest([2, 3, 7, 4])); // 3
Since you already sorted the array, it seems like you should be fine iterating over the list and keep track of the numbers you have already seen. When you have seen three different numbers, return the current one:
var seen = [arr[0]];
for (var i = 1; i < arr.length; i++) {
if (arr[i] !== seen[0]) {
if (seen.length === 2) {
return arr[i];
}
seen.unshift(arr[i]);
}
}
function thirdGreatest (arr) {
arr.sort(function(a, b) {
return b - a;
});
var seen = [arr[0]];
for (var i = 1; i < arr.length; i++) {
if (arr[i] !== seen[0]) {
if (seen.length === 2) {
return arr[i];
}
seen.unshift(arr[i]);
}
}
}
console.log(thirdGreatest([5, 3, 23, 7,3,2,5,10,24,2,31, 31, 31])); // 23
console.log(thirdGreatest([5, 3, 23, 7,3,2,5,10,24,2,31])) // 23
console.log(thirdGreatest([5, 3, 7, 4])); // 4
console.log(thirdGreatest([2, 3, 7, 4])); // 3
Note: You can simplify the sort callback to
arr.sort(function(a, b) {
return b - a;
});
// With arrow functions:
// arr.sort((a, b) => b - a);
The callback has to return a number that is larger, smaller or equal to 0, it doesn't have to be exactly -1 or 1.
A one-"line"r using Set to remove duplicates
Array.from(new Set(arr)).sort(function(a, b) {
return b - a;
})[2];
Set now has reasonable browser support
The optimal solution is to do this in a single pass O(n) time. You do not need to sort the array - doing so makes your solution at-least (n log n).
To do this in as single pass, you simply need three temporary variables: largest, secondLargest, thirdLargest. Just go through the array and update these values as necessary (i.e. when you replace largest it becomes second largest, etc...). Lastly, when you see duplicates (i.e. currentValue == secondLargest), just ignore them. They don't affect the outcome.
Don't forget to check for edge cases. You cannot provide an answer for [2, 2, 2, 2, 2] or [3, 2].
Try to think about what data structure you can use here. I suggest a set. Every time you add a nested loop your function gets exponentially slower.
Edited:
function thirdGreatest(arr) {
var s = Array.from(new Set(arr)).sort(function(a, b) {
return a - b;
})
return s[2] || s[1] || s[0] || null;
}
Working Example
We need to be able to handle:
[1,2,1,2] // 2
[1,1,1,1] // 1
[] // null
This assumes that you get an array passed in.
If you do not have a third largest number, you get the second.
If you do not have a second largest you get the first largest.
If you have no numbers you get null
If you want the 3rd largest or nothing, return s[2] || null
Many of the other answers require looping through the initial array multiple times. The following sorts and deduplicates at the same time. It's a little less terse, but is more performant.
const inputArray = [5,3,23,24,5,7,3,2,5,10,24,2,31,31,31];
const getThirdGreatest = inputArray => {
const sorted = [inputArray[0]]; // Add the first value from the input to sorted, for our for loop can be normalized.
let migrated = false;
let j;
for(let i = 1; i<inputArray.length; i++) { // Start at 1 to skip the first value in the input array
for(j=0; j<sorted.length; j++) {
if(sorted[j] < inputArray[i]) {
// If the input value is greater than that in the sorted array, add that value to the start of the sorted array
sorted.splice(j,0,inputArray[i]);
migrated = true;
break;
} else if(sorted[j] === inputArray[i]) {
// If the input value is a duplicate, ignore it
migrated = true;
break;
}
}
if(migrated === false) {
// If the input value wasn't addressed in the loop, add it to the end of the sorted array.
sorted[sorted.length] = inputArray[i];
} else {
migrated = false;
}
}
// Return the third greatest
return sorted[2];;
};
const start = performance.now();
getThirdGreatest(inputArray); // 23
const end = performance.now();
console.log('speed: ', end - start); // 0.1 - 0.2ms
One single iteration O(n) and very fast method of doing this is making your own Set like object. The advantageous point is making no comparisons at all when constructing our "sorted" list of "unique" elements which brings enormous efficiency. The difference is very noticeable when dealt with huge lists like in the lengths exceeding 1,000,000.
var arr = [5, 3, 23, 7,3,2,5,10,24,2,31, 31, 31],
sorted = Object.keys(arr.reduce((p,c)=> (p[c] = c, p),Object.create(null))),
third = sorted[sorted.length-3];
document.write(third);
If you think Object.keys might not return a sorted array (which i haven't yet seen not) then you can just sort it like it's done in the Set method.
Here i tried it for 1,000,000 item array and returns always with the correct result in around 45msecs. A 10,000,000 item array would take like ~450msec which is 50% less than other O(n) solutions listed under this question.
var arr = [],
sorted = [],
t0 = 0,
t1 = 0,
third = 0;
for (var i = 0; i<1000000; i++) arr[i] = Math.floor(Math.random()*100);
t0 = performance.now();
sorted = Object.keys(arr.reduce((p,c)=> (p[c] = c, p),Object.create(null)));
third = sorted[sorted.length-3];
t1 = performance.now();
document.write(arr.length + " size array done in: " + (t1-t0) + "msecs and the third biggest item is " + third);
I am trying to get this function to split an array into subsets. each subset is to have numbers that are equal to the previous or within 1 from the previous number.
The example I have below should return two subsets but it returns {0, 1, 2, 3} instead. Any idea on what I am doing wrong? Also, is there a better way to dynamically create an array for each new subset? Thanks
function max_tickets() {
var arr = [4, 13, 2, 3];
var myarr = arr.sort(function(a, b){return a-b});
for(var i = 0; i<myarr.length; i++){
var iplus = i+1;
if(i === i || i === iplus){
newArr= [];
newArr.push(i);
}else if (i !== i || i !== iplus){
arr2 =[];
arr2.push(i);
}
}
}
What you are trying to do is usually called "partitioning". The generic version of the problem is to partition an array into sub-arrays using some "rule", or predicate, or condition, which specifies which partition a particular element is supposed to go into, or specifies that it should go into a new partition.
The pseudo code for doing this would be:
To partition an array:
Initialize the resulting array
For each element in the array
If that element starts a new chunk
Create a new empty chunk in the resulting array
Add the element to the most recent chunk
Return the result
This can be expressed in JS quite straightforwardly as
function partition(array, fn) {
return array.reduce((result, elt, i, a) => {
if (!i || !fn(elt, i, a)) result.push([]);
result[result.length - 1].push(elt);
return result;
}, []);
}
Now we need to write the function saying when a new partition should start:
// Is the element within one of the previous element?
function close(e, i, a) {
return Math.abs(e - a[i-1]) > 1;
}
We can now partition the array with
partition([[4, 13, 2, 3], close)
This should work.
function max_tickets() {
var arr = [4, 13, 2, 3];
var myarr = arr.sort(function (a, b) { return a - b });
arrSubsets = [];
arr1 = [];
for (var i = 0; i < myarr.length; i++) {
if (myarr[i - 1] === undefined) {
arr1.push(myarr[i]);
continue;
}
if (myarr[i] - myarr[i - 1] <= 1) {
arr1.push(myarr[i]);
}
else {
arrSubsets.push(arr1);
arr1 = [];
arr1.push(myarr[i]);
}
}
if (arr1.length > 0)
arrSubsets.push(arr1);
}
max_tickets();
Based on your questions:
Any idea on what I am doing wrong?.
Inside of your loop you are using i as if it is the value of the array, but the loop goes from 0 to the value of myarr.length in your particular case 4, so that makes the value of i to be 0, 1, 2, 3.
As you can see you are using the values of the index to compare, instead of using the values of the array in order to use the values of the array you must specify the arrayname[index], in your case myarr[i] that will give you the values: 4, 13, 2, 3.
Also, is there a better way to dynamically create an array for each new subset?
Yes you can create an array inside of another array dynamically inside of a loop:
var b = [];
for(var i = 0; i < 10; i++){
b.push(['I am' + i, i]);
}
As you can see in the previous example I'm creating an array inside of the b array so once the loop finishes the b array will have 10 arrays inside of it with 2 elements each.
What I need to know is a way to get the current index in the compare function of sort method in an array. Suppose this code:
var points = [40, 100, 1, 5, 25, 10];
points.sort(function (a, b) {
return a - b;
//i need the current index of `a` and `b` here
});
In the anonymous function, I need the current index of a and b objects. How can I get it?
Make an array of objects with indices...
var arr = [1,5,2,3,8,13, 1];
var arr2 = arr.map(function(o, i) {return {idx: i, obj: o}; }).sort(function(a, b) {
console.log('a.idx:', a.idx, 'b.idx:', b.idx);
return a.obj - b.obj;
});
for(var i = 0, j = arr2.length; i < j; i++){
console.log('i:', i, 'arr2[i].obj:', arr2[i].obj);
}
fiddle: https://jsfiddle.net/k6xdqpb2/
The index of a or b?
var indexOfA = points.indexOf(a);
This will give you the index that a first appears in the array.
Depending on what you are trying to do, another option may be to use a map for sorting as described in more details under the 'Sorting with map' heading here. An example of this approach is given in deostroll's answer.
I wanted to change the rows into columns of an array.
[
[1],
[1,2],
[1,2,3],
[4,2,3],
[4,5,3],
[4,5,6]
]
to
[
[1,1,1,4,4,4],
[2,2,2,5,5],
[3,3,3,6]
]
I tried
var res = [];
for(i in this.fields) {
for(j in this.fields[i].value) {
if(i === 0) res[j] = [];
res[j][i] = this.fields[i].value[j];
}
}
this gives me empty set.
Create this function:
function transpose(arr) {
return Object.keys(arr[0]).map(function (c) {
return arr.map(function (r) {
return r[c];
});
});
}
and then:
var transposedArray = transpose(originalArray);
What you're asking looks a little weird because you have different lengths and you're ignoring undefined values, but it is still achievable.
Don't use for..in loops for Array, use a normal for. Also, you'll need to know how many items you'll have in your new parent Array, which is the max of the lengths of the original child Arrays.
var arrR = [ // will refer to "down" and "across" as in this literal
[1],
[1, 2],
[1, 2, 3],
[4, 2, 3],
[4, 5, 3],
[4, 5, 6]
];
function r2c(arr) {
var arrC = [], // next get the longest sub-array length
x = Math.max.apply(Math, arr.map(function (e) {return e.length;})),
y = arr.length,
i, j;
for (i = 0; i < x; ++i) { // this is the loop "down"
arrC[i] = [];
for (j = 0; j < y; ++j) // and this is the loop "across"
if (i in arr[j])
arrC[i].push(arr[j][i]);
}
return arrC;
}
var arrC = r2c(arrR);
/* [
[1, 1, 1, 4, 4, 4],
[2, 2, 2, 5, 5],
[3, 3, 3, 6]
] */
You should still consider if you're happy with [[1], [1, 2], [1]] becoming [[1, 1, 1], [2]], which I would consider unexpected (the position of 2 is completely lost), but seems to be what you intend.
Similar to Pauls but doesn't need to get the max length first:
function transpose(arr) {
// Loop over arrays as long as one has values
// Arrays should be contiguous, may fail if sparse
for (var result = [], i=0, more; more; i++) {
more = false;
// Get the ith element of each array (if there is one)
for (var j=0, jLen=arr.length; j<jLen; j++) {
// Don't add missing members
if (arr[j].hasOwnProperty(i)) {
// Add array for result if not already there
result[i] = result[i] || [];
// Do transpose
result[i][j] = arr[j][i];
// Only keep going while there is data
more = true;
}
}
}
return result;
}
BTW, a fixed version of your original function is:
function transpose2(fields) {
// Make sure the result array is initialised
var res = [];
// Don't forget to keep counters local - declare them
// I've removed *this* as it's a plain function, use it if
// it's an instance method
for(var i in fields) {
// Values are read directly, there is no "value" accessor
for(var j in fields[i]) {
// Don't rely on order of enumeration - may not start at 0
if(!res[j]) res[j] = [];
// Do the transpose
res[j][i] = fields[i][j];
}
}
return res;
}
But as noted above, for..in is not liked for arrays, particularly as there are many libraries that extend built-ins like Array.prototype so you will traverse those properties too. But if you're cool with that, this is a good way to deal with sparse arrays. You can add a hasOwnProperty test to avoid inherited enumerables.
Note also that the order of enumeration isn't necessarily from '0' or in any particular order, hence changed way of initialising res[j].
Is there a way to compare an integer against an array of integers? For instance, to determine if an int is larger than any of the array ints?
var array = [1, 2, 3, 4];
if(5 > array){
// do something
}
Update: I guess I meant, is 5 larger than the largest number in the array. Thanks!
You can use Math.max and apply
if (5 > Math.max.apply(Math, array)) {
// do something
}
Update: To explain as it works. It's described in the docs I linked but I will try to be more clear here:
Math.max returns the largest of zero or more numbers, so:
Math.max(1, 2, 3, 4) // returns 4
apply calls a function with a given 'this' value (the first argument) and arguments provided as an array (the second). so:
function sum(a, b) {
return a + b;
}
console.log(sum.apply(window, [2, 3])); // 5
Therefore, if you have an array of integers and you want to get the max, you can combine them to have:
console.log(Math.max.apply(Math, [1, 2, 3, 4])); // 4
Because it's exactly like have:
console.log(Math.max(1, 2, 3, 4));
The difference is you pass an array instead.
Hope it's more clear now!
There is no good and readable built in way of doing it, but it can be done simply with:
var bigger = true;
for (var i =0; i < array.length; i++) {
if (5 <= array[i]) {
bigger = false;
// you can add here : break;
}
}
Sure, you could sort the array, take the last element, and see if your integer is greater than that. Or, you could loop through and check each one. Up to you. The loop is a more performant way.
//maybe check the bounds on this if you know it can be a blank array ever
var max = myArray[0];
for(var x = 0; x < myArray.length; x++) {
max = Math.max(max, myArray[x]);
}
if(500 > max) {
//do something
}