8

I am using Math.min to get the smallest number out of an array of numbers. However, I also need to get the second smallest number as well. Wondering if there is a way to do this using Math.min as well, otherwise looking for the best way to get this number.

Here's what I have:

var arr = [15, 37, 9, 21, 55];
var min = Math.min.apply(null, arr.filter(Boolean));
var secondMin; // Get second smallest number from array here

console.log('Smallest number: ' + min);
console.log('Second smallest number: ' + secondMin);
user13286
  • 3,027
  • 9
  • 45
  • 100

15 Answers15

6

just to make that thread completed: the fastest way is to iterate all the elements just like you can do to find minimum. But up to your needs there will be two variables used: first minimum (candidate) and second one.

This logic is O(N) while sorting approach is O(N lg(N)).

But maybe you shouldn't care if this is just for practice.

In case repeatition should be processed as independant value(just like it would be for .sort(...)[1]) then it should <= be used instead of <.

var arr = [15, 37, 9, 21, 55];
var min = Infinity, secondMin = Infinity; 
for (var i= 0; i< arr.length; i++) {
    if (arr[i]< min) {
        secondMin = min;
        min = arr[i]; 
    } else if (arr[i]< secondMin) {
        secondMin = arr[i]; 
    }
}

console.log('Smallest number: ' + min);
console.log('Second smallest number: ' + secondMin);
skyboyer
  • 22,209
  • 7
  • 57
  • 64
  • 1
    Doesn't work with repeats. You actually need to partition the array. I will give you a counter example input to any function you write this way. – Mad Physicist Nov 16 '17 at 00:07
  • Partitioning is O(n) as well btw – Mad Physicist Nov 16 '17 at 00:07
  • @MadPhysicist, added code snippet. Maybe I misunderstood root question. Does not author want to get 15 for `[15, 37, 9, 21, 55]`? – skyboyer Nov 16 '17 at 09:51
  • Sure, but he wants 9 for `[15, 37, 9, 21, 55, 9]` – Mad Physicist Nov 16 '17 at 14:55
  • There seems to be a difference between what javascript calls partitioning and what numpy refers to with that term. I am referring to the numpy definition. – Mad Physicist Nov 16 '17 at 14:57
  • 1
    Now that I think about it, your approach would work if you retained the indices and changed your first check to `<= min`. – Mad Physicist Nov 16 '17 at 15:13
  • @MadPhysicist sorry, I did not get it, why is it needed to retain indicies? – skyboyer Nov 16 '17 at 15:41
  • @MadPhysicist could you pleas give a hint how partitioning could help solving that in O(N)? – skyboyer Nov 16 '17 at 15:43
  • 1
    Partitioning in the numpy sense is finding the n smallest elements of the array. The most common algorighm for this is [quickselect](https://en.wikipedia.org/wiki/Quickselect) It works like quicksort, but only for the half of the array in each recursion. Since `n + n/2 + n/4 + ...` sums to `2n`, it is an `O(n)` algorightm. – Mad Physicist Nov 16 '17 at 16:20
  • I've flipped my vote. You are right that there is no need for retaining indices if you are doing a one-pass evaluation. Your current implementation works even with repeated elements. My bad for thinking otherwise. – Mad Physicist Nov 16 '17 at 19:56
5

I see you are using Array.filter (but why?) on your first min. So, if we are using ES6 features you can find the second lowest value by first removing min from the array.

var secondMin = Math.min.apply(null, arr.filter(n => n != min));

edit: for clarity, unless you are doing something very clever with Array.filter(Boolean) while calculating the first min, you should just pass it the array without filtering:

var min = Math.min.apply(null, arr);
cstricklan
  • 662
  • 4
  • 13
3

If you are not worried about performance you could simply sort the array.

arr.sort(function(a, b) {
  return a - b;
});
Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
  • 1
    @MadPhysicist He simply stated "best". As for readability I think it is. I also explicitly stated "If you are not worried about performance". Do I need to bold it as well? – Emil Eklund Nov 15 '17 at 20:33
  • I haven't been able to find a JavaScript equivalent to numpy's partition function, so this is likely as elegant as it will get. – Mad Physicist Nov 15 '17 at 20:41
3

@skyboyer provides what is probably the fastest algorithm for finding the two smallest elements. Another type algorithm that runs in O(n) time is selection (in the general comp-sci definition, not how it is normally used in JavaScript), which will find the kth smallest element of an array and separate the elements into those larger and those smaller than the kth.

Even though most partitioning selection algorithms (quickselect, Floyd-Rivest, introselect) run in O(n) time, @skyboyer's answer will be faster because of the specific nature of the partition you are looking for, and because all those algorithms come with a heavy constant factor.

There is a javascript library implementing Floyd-Rivest, but named quickselect that can do the partitioning for you, in place:

quickselect(arr, 1)

arr will be rearranged so that arr[0] is the minimum, arr[1] is the second smallest element, and the remaining elements are in some arbitrary order.

Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
1

ES6 solution with reduce (very performant)

const A = [8, 24, 3, 20, 1, 17]
const min = Math.min(...A)
const secondMin = A.reduce((pre, cur) => (cur < pre && cur !== min) ? cur : pre
  , Infinity)
console.log(min, secondMin)
Thomas Gotwig
  • 3,659
  • 3
  • 17
  • 15
1

This the @skyboyer version that will handle repeats correctly:

function secondSmallest(x) {
  if (x.length < 2) return 0;

  let first = Number.MAX_VALUE;
  let second = Number.MAX_VALUE;

  for (let i = 0; i < x.length; i++) {
    let current = x[i];
    if (current < first) {
      second = first;
      first = current;
    } else if (current < second && current !== first) {
      second = current;
    }
  }
  return second;
}

console.log(secondSmallest([1, 1, 1, 1, 2]));
console.log(secondSmallest([1, 2, 1, 1, 1]));
console.log(secondSmallest([6, 3, 4, 8, 4, 5]));
1
var arr=[1,0,-1,-2,-8,5];
var firstmin=arr[0];
var secondmin=arr[1];
for(i=0;i<=arr.length;i++) {
     if(arr[i]<firstmin){
          secondmin=firstmin;
          firstmin=arr[i];
     }
     else if(i>=1 && arr[i]<secondmin) {
         secondmin=arr[i];
     }
}

console.log(firstmin);
console.log(secondmin);

var arr = [15, 37, 9, 21, 55];
var min = Infinity, secondMin = Infinity; 
for (var i= 0; i< arr.length; i++) {
    if (arr[i]< min) {
        secondMin = min;
        min = arr[i]; 
    } else if (arr[i]< secondMin) {
        secondMin = arr[i]; 
    }
}

console.log('Smallest number: ' + min);
console.log('Second smallest number: ' + secondMin);
PrakashG
  • 1,642
  • 5
  • 20
  • 30
1
var arr=[1,1,1,1,1,-1,-2];
var firstmin=arr[0];
var secondmin=arr[1];
for(i=0;i<=arr.length;i++){
  if(arr[i]<firstmin){
    secondmin=firstmin;
    firstmin=arr[i];

  }else if(i>=1 && arr[i]<secondmin) {
    secondmin=arr[i];
  }

}
console.log(firstmin);
console.log(secondmin);`
1

A recursive approach with ternary operators would look like this. If the array has duplicates it would give you a non duplicated second min.

For example if you have have [1, 1, 2] the second min would be 2 not 1.

function findMinimums(arr, min, secondMin, i) {
  if (arr.length === i) {
    return {
      min,
      secondMin
    }
  }
  return findMinimums(
    arr,
    min = arr[i] < min ? arr[i] : min,
    arr[i] < secondMin && min !== arr[i] ? arr[i] : secondMin,
    ++i
  )
}

const arr = [5, 34, 5, 1, 6, 7, 9, 2, 1];
console.log(findMinimums(arr, arr[0], arr[1], 0))
EugenSunic
  • 13,162
  • 13
  • 64
  • 86
1

var arr = [15, 37, 9, 21, 55];
const [secondMin, min] = arr.sort((a,b) => b - a).slice(-2)

console.log('Smallest number: ' + min);
console.log('Second smallest number: ' + secondMin);
miorey
  • 828
  • 8
  • 19
0
function secondSmallest(arr){
  let firstSmallest = Infinity, secondSmallest = Infinity; 
  // if the length is 1; return the element
  if(arr.length == 1){
    return arr[0]
  }
  for (let i= 0; i< arr.length; i++) {
      if (arr[i]< firstSmallest) {
          secondSmallest = firstSmallest;
          firstSmallest = arr[i]; 
      } else if (arr[i]< secondSmallest) {
          secondSmallest = arr[i]; 
      }
  }

  console.log(firstSmallest, secondSmallest)
  return secondSmallest
}
Joomler
  • 2,610
  • 3
  • 30
  • 37
0

function findTwoSmallestNumbersFromArray(num) {
let min = Math.min(...num) let secondMin = Math.min(...num.filter(numbers => !min.toString().includes(numbers)))

console.log(min, secondMin) }

0

Yes, it's possible. But rather than directly using Math.min() we have to use Math.min.apply(), as we know Math.min() wouldn't take an array. Here's the solution which will work even if there's duplicates of smallest number in the array.

const findSecondSmallest = function (arr) {
  const smallest = Math.min.apply(null, arr);

  /* 
  To remove duplicates too I compared the smallest number through loop. 
  Otherwise You can direcly set the value of smallest element to 'Infinity'
  like this-

  arr[arr.indexOf(smallest)] = Infinity;
  */ 

  for (let i = 0; i < arr.length; i++) {
    if (smallest === arr[i]) {
      arr[i] = Infinity;
    }
  }
  const secondSmallest = Math.min.apply(null, arr);
  return secondSmallest;
};

console.log(findSecondSmallest([3, 3, 3, 5, 5, 7, 9, 11, 13])); //5
0

The Best way to get the second minimum value from an array.

let array = [5, 8, 3, 8, 2, 6, 7, 10, 2];
let minValue = Math.min(...array);
let secondMinVAlue = Math.min(...array.filter((v) => v !== minValue));
console.log(secondMinVAlue);

-1

let num = [4,12,99,1,3,123,5];
let sort = num.sort((a,b) => {return a-b})

console.log(sort[1])