Trying to find the second largest value in a javascript array - javascript

I am trying to find the second largest value in an array but my code returns the second smallest value in an array. Any pointers as to where i'm missing it? The code is suppose to simply loop backwards through the array, matching any value that is less than the highest value and also "one less than" the highest value.
function getSecondLargest(nums) {
let sNums = nums.sort();
let max = sNums.length - 1;
for (let i = sNums.length; i > 0; i--) {
if (sNums[i] < max && (sNums[i] === (max - 1))) {
return sNums[i];
}
}
}
console.log(getSecondLargest([8,7,9,4,5,6,3,2.10,22,42,101]))

You can sort it by descending and take the second item.
function getSecondLargest(nums) {
return nums.sort((n1,n2) => n2-n1)[1];
}
console.log(getSecondLargest([10,20,40,30,80,90]));
If there are duplicates, you can use this.
function getSecondLargest(nums) {
return nums.sort((n1,n2) => n2-n1).filter(function(item, pos, ary) {
return !pos || item != ary[pos - 1];
})[1];
}
console.log(getSecondLargest([10,20,40,30,80,90,90]));
The issue with Tino's code is finding the second max number. It should be let max = sNums[sNums.length - 1]; instead of let max = sNums.length - 1;, the loop should start at sNums.length - 1and it goes until i >= 0, and the if statement just needs to compare the current value with the max value. If a second largest is not found return null. Null means all the elements in the array have the same value.
function getSecondLargest(nums) {
let sNums = nums.sort((a,b) => a-b);
let max = sNums[sNums.length - 1];
for (let i = sNums.length - 1; i >= 0; i--) {
if (sNums[i] < max) {
return sNums[i];
}
}
return null;
}
console.log(getSecondLargest([8,7,9,9,4,5,6,3,2]))

Ok, my suggestion - ES6 to the rescue
See other answers and comment why original code did not work
const getSecondLargest = numArr => Array.from(new Set(numArr)) // makes array of unique entries
.sort((a,b) => b-a)[1]; // sorts in descending numerical order
console.log(getSecondLargest([8,7,9,4,5,6,3,2.10,22,42,101]))

If you want to avoid sort method. Here's the simplest solution IMO. It'll also work if there's duplicate numbers of largest integer.
function getSecondLargest(arr) {
const largest = Math.max.apply(null, arr);
for (let i = 0; i < arr.length; i++) {
if (largest === arr[i]) {
arr[i] = -Infinity;
}
}
return Math.max.apply(null, arr);
}
console.log(getSecondLargest([2, 3, 4, 4, 4])); //3

Related

Find First and Last Position of Element in Sorted Array

I am trying to solve the LeetCode problem Find First and Last Position of Element in Sorted Array:
Given an array of integers nums sorted in ascending order, find the starting and ending position of a given target value.
If target is not found in the array, return [-1, -1].
You must write an algorithm with O(log n) runtime complexity.
I am writing this code:
var searchRange = function(nums, target) {
nums = nums.sort((a, b) => (a-b))
let result = [];
for(let i = 0; i < nums.length; i++) {
if (nums[i] == target) {
result.push(i)
} else {
result = [-1, -1]
}
}
return result
};
console.log(searchRange([5,7,7,8,8,10], 8));
It should return:
[3, 4]
but it returns:
[-1, -1]
What is wrong?
Your else code is wiping out results from the previous iteration of the loop.
However, your code is not efficient:
First, it calls sort on an array that is given to be already sorted: so leave that out.
Secondly, as your code is visiting every element in the array, you still get a time complexity of O(n), not O(logn).
To get O(logn) you should implement a binary search, and then perform a search to find the start of the range, and another to find the end of it.
Here is how that could work:
function binarySearch(nums, target) {
let low = 0;
let high = nums.length;
while (low < high) {
let mid = (low + high) >> 1; // half way
if (nums[mid] > target) {
high = mid;
} else {
low = mid + 1;
}
}
return high; // first index AFTER target
}
function searchRange(nums, target) {
let end = binarySearch(nums, target);
let start = binarySearch(nums, target - 1);
return start === end ? [-1, -1] : [start, end - 1];
}
console.log(searchRange([5,7,7,8,8,10], 8));
You loop does not have a good exit condition. Loop iterates till the last element and if the last element does not match, it sets -1,-1 into the result.
Intermediate values of result are ignored.
Other problems:
You're given a sorted array, so don't sort it again. That's heavy.
When you iterate over all elements, it has O(n) time complexity.
Solution:
Binary search to find any one occurence of the element - save the index where the value is found.
Use the index found to check left and right adjacent elements in the original array to find the start and end positions.
function searchRange(r,n){
var s = r.sort((a,b)=>a-b);
return [s.indexOf(n), s.lastIndexOf(n)];
}
console.log('result 1',searchRange([5,7,7,8,8,10], 8))
console.log('result 2',searchRange([5,7,7,6,6,10], 8))
You were on the right track, its just your logic. You are setting result to [-1, -1] if ANY index of nums after the last target index is not equal to the target. So you want to have the check outside of the for loop.
var searchRange = function(nums, target) {
nums = nums.sort((a, b) => (a-b));
let result = [];
for(let i = 0; i < nums.length; i++) {
if (nums[i] == target) {
result.push(i);
}
}
return (result.length == 0) ? [-1, -1] : result;
};
console.log(searchRange([5,7,7,8,8,10], 8));
Note: This will not be the final solution because this will return EVERY index that contains target
Dart
if (nums.isEmpty || !nums.contains(target)) {
return [-1, -1];
} else {
return [nums.indexOf(target), nums.lastIndexOf(target)];
}

Why i cant use for this way?

The plan is:
sort 'arr' from less to greater.
push values in range of both number from arr, inclusively.
find smallest common number which dividible on each value from 'arrForAll' without reminder.
Assign to 'i' first value from 'arrForAll'.
Output 'while' condition isn't false. Condition is: 'i' don't divide on each value from 'arrForAll' without reminder.
I tried this:
function smallestCommons(arr) {
let sortedArray = arr.sort();
let arrForAll = []
for (let i = sortedArray[0]; i < sortedArray[1] + 1; i++) {
arrForAll.push(i)
}
for (let i = sortedArray[1]; arrForAll.every(x => i % x !== 0); i += i) {
console.log(i)
}
return arr;
}
console.log(smallestCommons([1, 5]));
But console output nothing.
What's wrong in code?
In conditioin of for loop it must use logic operators '<','>', etc.
So this situation while loop is more suitable solution
function smallestCommons(arr) {
var i=0;
let sortedArray = arr.sort();//here store sorted value
let arrForAll=[]
for(let i=sortedArray[0];i<sortedArray[1]+1;i++){
arrForAll.push(i)
}//store [x to y]
while(true){
i+=sortedArray[1];
if(arrForAll.every(x => i % x == 0)){
break;
}
}
return i;}
console.log(smallestCommons([23, 18]));//returns 6056820, [1,5] will return 60

How to find the number of subarrays in an array with given sum?

My program should be as following:
Input : {1,2,3,2,1,8,-3}, sum = 5
Output should be 3 example combinations ({2,3}, {3,2}, {8,-3}) have sum
exactly equal to 5.
I tried to do it in JavaScript but I'm confused.
function findSubarraySum(arr, sum) {
var res = 0;
var currentSum = 0;
for (var i = 0; i < arr.length; i++) {
currentSum += arr[i];
if (currentSum == sum)
res++;
}
return res;
}
console.log(findSubarraySum([1, 2, 3, 4], 10));
You first need a way to iterate over all the unique ways you can choose a start and and of your subarray boundaries (your slice definition).
In my code below, I use a combinations function to get all possible combinations of two indexes for the array supplied. You could do something else, like a simple doubly nested for loop.
Next you need to take the slice of the array according to the slice definition and reduce the elements into a sum. The Array.prototype.reduce function works well for that.
Finally, you want to include the subArray in the list of results only if the reduced sum matched the desired sum.
// Input : {1,2,3,2,1,8,-3}, sum = 5
const { combinations, range } = (() => {
const _combinations = function*(array, count, start, result) {
if (count <= 0) {
yield [...result]; // Yes, we want to return a copy
return;
}
const nextCount = count - 1;
const end = array.length - nextCount; // leave room on the array for the next remaining elements
for (let i = start; i < end; i += 1) {
// we have already used the element at (start - 1)
result[result.length - count] = array[i];
const nextStart = i + 1; // Always choose the next element from the ones following the last chosen element
yield* _combinations(array, nextCount, nextStart, result);
}
};
function* combinations(array, count) {
yield* _combinations(array, count, 0, Array(count));
}
function* range(l) {
for (let i = 0; i < l; i += 1) {
yield i;
}
}
return {
combinations,
range,
};
})();
const subArraysBy = (predicate, array) => {
const result = [];
for (const [beg, end] of combinations([...range(array.length+1)], 2)) {
const subArray = array.slice(beg, end);
if (predicate(subArray)) {
result.push(subArray);
}
}
return result;
};
const sum = array => array.reduce((sum, e) => sum + e);
console.log(
subArraysBy(
a => sum(a) === 5,
[1, 2, 3, 2, 1, 8, -3],
),
);
References:
MDN: Array.prototype.reduce
MDN: function* -- not required for your solution
Lodash: _.range -- implemented this in my code rather than use the lodash one. They work similarly.
Python Docs: combinations - My combinations implementation is inspired by python itertools.

An algorithm to find the closest values in javascript array

I'm working on a small algorithm to find the closest values of a given number in an random array of numbers. In my case I'm trying to detect connected machines identified by a 6-digit number ID ("123456", "0078965", ...) but it can be useful for example to find the closest geolocated users around me.
What I need is to list the 5 closest machines, no matter if their IDs are higher or lower. This code works perfectly but I'm looking for a smarter and better way to proceed, amha I got to much loops and arrays.
let n = 0; // counter
let m = 5; // number of final machines to find
// list of IDs founded (unordered: we can't decide)
const arr = ["087965","258369","885974","0078965","457896","998120","698745","399710","357984","698745","789456"]
let NUM = "176789" // the random NUM to test
const temp = [];
const diff = {};
let result = null;
// list the [m] highest founded (5 IDs)
for(i=0 ; i<arr.length; i++) {
if(arr[i] > NUM) {
for(j=0 ; j<m; j++) {
temp.push(arr[i+j]);
} break;
}
}
// list the [m] lowest founded (5 IDs)
for(i=arr.length ; i>=0; i--) {
if(arr[i] < NUM) {
for(j=m ; j>=0; j--) {
temp.push(arr[i-j]);
} break;
}
}
// now we are certain to get at least 5 IDs even if NUM is 999999 or 000000
temp.sort(function(a, b){return a - b}); // increase order
for(i=0 ; i<(m*2); i++) {
let difference = Math.abs(NUM - temp[i]);
diff[difference] = temp[i]; // [ 20519 : "964223" ]
}
// we now get a 10-values "temp" array ordered by difference
// list the [m] first IDs:
for(key in diff){
if(n < m){
let add = 6-diff[key].toString().length;
let zer = '0'.repeat(add);
let id = zer+diff[key]; // "5802" -> "005802"
result += (n+1)+":"+ id +" ";
n+=1;
}
}
alert(result);
-> "1:0078965 2:087965 3:258369 4:357984 5:399710" for "176789"
You actually don't need to have so many different iterations. All you need is to loop twice:
The first iteration attempt is to use .map() to create an array of objects that stores the ID and the absolute difference between the ID and num
The second iteration attempt is simply to use .sort() through the array of objects created in step 1, ranking them from lowest to highest difference
Once the second iteration is done, you simply use .slice(0, 5) to get the first 5 objects in the array, which now contains the smallest 5 diffs. Iterate through it again if you want to simply extract the ID:
const arr = ["087965","258369","885974","078965","457896","998120","698745","399710","357984","698745","789456"];
let num = "176789";
let m = 5; // number of final machines to find
// Create an array of objects storing the original arr + diff from `num`
const diff = arr.map(item => {
return { id: item, diff: Math.abs(+item - +num) };
});
// Sort by difference from `num` (lowest to highest)
diff.sort((a, b) => a.diff - b.diff);
// Get the first m entries
const filteredArr = diff.slice(0, m).map(item => item.id).sort();
// Log
console.log(filteredArr);
// Completely optional, if you want to format it the way you have in your question
console.log(`"${filteredArr.map((v, i) => i + ": " + v).join(', ')}" for "${num}"`);
You could take an array as result set, fill it with the first n elements and sort it by the delta of the wanted value.
For all other elements check if the absolute delta of the actual item and the value is smaller then the last value of the result set and replace this value with the actual item. Sort again. Repeat until all elements are processed.
The result set is ordered by the smallest delta to the greatest by using the target value.
const
absDelta = (a, b) => Math.abs(a - b),
sortDelta = v => (a, b) => absDelta(a, v) - absDelta(b, v),
array = [087965, 258369, 885974, 0078965, 457896, 998120, 698745, 399710, 357984, 698745, 789456],
value = 176789,
n = 5,
result = array.reduce((r, v) => {
if (r.length < n) {
r.push(v);
r.sort(sortDelta(value));
return r;
}
if (absDelta(v, value) < absDelta(r[n - 1], value)) {
r[n - 1] = v;
r.sort(sortDelta(value));
}
return r;
}, []);
console.log(result); // sorted by closest value
A few good approaches so far, but I can't resist throwing in another.
This tests a sliding window of n elements in a sorted version of the array, and returns the one whose midpoint is closest to the value you're looking for. This is a pretty efficient approach (one sort of the array, and then a single pass through that) -- though it does not catch cases where there's more than one correct answer (see the last test case below).
const closestN = function(n, target, arr) {
// make sure we're not comparing strings, then sort:
let sorted = arr.map(Number).sort((a, b) => a - b);
target = Number(target);
let bestDiff = Infinity; // actual diff can be assumed to be lower than this
let bestSlice = 0; // until proven otherwise
for (var i = 0; i <= sorted.length - n; i++) {
let median = medianOf(sorted[i], sorted[i+n-1]) // midpoint of the group
let diff = Math.abs(target - median); // distance to the target
if (diff < bestDiff) { // we improved on the previous attempt
bestDiff = diff; // capture this for later comparisons
bestSlice = i;
}
// TODO handle diff == bestDiff? i.e. more than one possible correct answer
}
return sorted.slice(bestSlice, bestSlice + n)
}
// I cheated a bit here; this won't work if a > b:
const medianOf = function(a, b) {
return (Math.abs(b-a) / 2) + a
}
console.log(closestN(5, 176789, ["087965", "258369", "885974", "0078965", "457896", "998120", "698745", "399710", "357984", "698745", "789456"]))
// more test cases
console.log(closestN(3, 5, [1,2,5,8,9])) // should be 2,5,8
console.log(closestN(3, 4, [1,2,5,8,9])) // should be 1,2,5
console.log(closestN(1, 4, [1,2,5,8,9])) // should be 5
console.log(closestN(3, 99, [1,2,5,8,9])) // should be 5,8,9
console.log(closestN(3, -99, [1,2,5,8,9])) // should be 1,2,5
console.log(closestN(3, -2, [-10, -5, 0, 4])) // should be -5, 0, 4
console.log(closestN(1, 2, [1,3])) // either 1 or 3 would be correct...

How to sort string numbers which consist of 3 point?

I have an array which consists of String numbers which consist of 3 point such as "1.452", "11.3.2","12".
For example if I sort this array
$scope.myArray=["11","14.2","9.2.1","5.6.3","0.9","6.7.2","2","1"];
I want to see this array like so
0.9,
1,
2,
5.6.3,
6.7.2,
9.2.1,
11,
14.2
I want to sort these values in descending order. How can I do this with AngularJS ?
Thank you in advance.
It's not pretty, but I think it works:
var myArray = ["11", "14.2", "9.2.1", "5.6.3", "0.9", "6.7.2", "2", "1"];
function compare(a, b) {
let aa = a.split(".");
let bb = b.split(".");
for (let i = 0; i < aa.length; i++) {
if (parseInt(aa[i]) > parseInt(bb[i])) {
return 1;
} else if (parseInt(aa[i]) < parseInt(bb[i])) {
return -1;
}
}
return -1;
}
myArray = myArray.sort(function(a, b) {
return compare(a, b)
})
console.log(myArray);
So all you need is to write your custom comparator for Array.prototype.sort() function. Let's look at the example below (I'll try to explain it in the comments)
const compareValues = (a, b) => {
// for each element which we compare we need to split it using '.' to get comparable integers
const aNumbers = a.split('.');
const bNumbers = b.split('.');
// then we start a look over the received arrays
for (let i = 0; i < aNumbers.length || i < bNumbers.length; i++) {
// here we take a current element. If we doesn't exist we consider it as 0
let currentA = parseInt(aNumbers[i]) || 0;
let currentB = parseInt(bNumbers[i]) || 0;
if (currentA === currentB) {
// if the numbers are equal, we go to the next pair of items
continue;
} else {
// otherwise we return the difference between them, which says to sort() function which one is bigger
return currentA - currentB;
}
}
// if nothing was returned from the above for statement all numbers are equal
return 0;
};
// here's the array, that you want to sort
const arr = ["11","14.2","9.2.1","5.6.3","0.9","6.7.2","2","1"];
// and just call the sort function with our comparator.
const sortedArr = arr.sort(compareValues);

Categories

Resources