1

I'm trying to write a function that finds the two biggest value inside an array of numbers and stores them inside a new array. I'm unable to first remove the first biggest number from the original array and then find the second biggest.

here is my code:

function choseBig (myArray) {
    //var myArray = str.split(" "); 
    var result = [];
    var firstBig;
    var secondBig;
        // select the biggest value
        firstBig = Math.max.apply(Math, myArray);
        // find its index
        var index = myArray.indexOf(firstBig);
        // remove the biggest value from the original array 
        var myArray_2 = myArray.slice((index -1), 1);
        // choose the second biggest value
        secondBig = Math.max.apply(Math, myArray_2);
        // push the results into a new array
        result.push(firstBig, secondBig);

    return result;
}

console.log(choseBig ([1,2,3,4,5,9]));  
zamzam
  • 347
  • 1
  • 2
  • 9
  • Use. `.splice()` to remove an item from an array. – jfriend00 Mar 05 '16 at 18:52
  • had a glance at all the answer below, I wonder why people suggest O(nlogn) function for a problem that can be easily solved using a for loop. =___= – Mox Mar 05 '16 at 19:09

8 Answers8

5

At first glance, I'd suggest:

function choseBig(myArray) {
  return myArray.sort((a, b) => b - a).slice(0, 2);
}

console.log(choseBig([1, 2, 3, 4, 5, 9]));

To extend the above a little, for example offering the user the option to specify whether the returned values should be the highest numbers, or the lowest numbers, and how many they wish returned, I'd offer the following:

function choseBig(myArray, opts) {
  // 'max':      Boolean,
  //             true:  returns the highest numbers,
  //             false: returns the lowest numbers
  // 'howMany':  Number,
  //             specifies how many numbers to return:
  var settings = {
      'max': true,
      'howMany': 2
    };

  // ensuring we have an Object, otherwise
  // Object.keys( opts ) returns an error:
  opts = opts || {};

  // retrieving the keys of the opts Object, and
  // uses Array.prototype.forEach() to iterate over
  // those keys; 'o' (in the anonymous function) is
  // the array element (the property-name/key) from
  // the array Object keys over which we're iterating:
  Object.keys(opts).forEach(function(o) {

    // updating the settings Object to the new values
    // (if any is specified) to those set in the user-
    // supplied opts Object:
    settings[o] = opts[o];
  });

  // here we first sort the Array, using a numeric sort;
  // using ES2015 Arrow functions. 'a' and 'b' are supplied
  // by Array.prototype.sort() and refer to the current ('a')
  // and next ('b') array-elements. If b - a is less than zero
  // b is moved to a lower index; if a - b is less than zero
  // a is moved to a lower index.
  // Here we use a ternary operator based on whether settings.max
  // is true; if it is true we sort to move the larger number to
  // the lower index; otherwise we sort to move the smaller number
  // to the lower index.
  // Then we slice the resulting array to return the numbers from
  // the 0 index (the first number) to the settings.howMany number
  // (the required length of the array).
  // this is then returned to the calling context.
  return myArray.sort((a, b) => settings.max === true ? b - a : a - b).slice(0, settings.howMany);
}

console.log(choseBig([1, 2, 3, 4, 5, 9], {
  // here we specify to select the largest numbers:
  'max': true,
  // we specify we want the 'top' three numbers:
  'howMany': 3
}));

function choseBig(myArray, opts) {
  var settings = {
    'max': true,
    'howMany': 2
  };

  opts = opts || {};

  Object.keys(opts).forEach(function(o) {
    settings[o] = opts[o];
  });

  return myArray.sort((a, b) => settings.max === true ? b - a : a - b).slice(0, settings.howMany);
}

console.log(choseBig([1, 2, 3, 4, 5, 9], {
  'max': true,
  'howMany': 3
}));

JS Fiddle demo.

References:

Community
  • 1
  • 1
David Thomas
  • 249,100
  • 51
  • 377
  • 410
  • Conceptually good idea, but this implementation is flawed for multiple reasons. You have to call `.sort()`. You want the last two items, you have to implement a true numeric sort. – jfriend00 Mar 05 '16 at 18:53
  • 1
    ^^ Sort is lexicographic by default, so `[1,2,3,4,33].sort() == [1,2,3,33,4]`, no good. – elclanrs Mar 05 '16 at 18:54
  • @jfriend00: it's been a ridiculously long day (which is not an excuse for posting a wrong answer), what are the flaws you see that I'm obviously missing? Other than validating that the array elements are numbers? – David Thomas Mar 05 '16 at 18:55
  • @DavidThomas - Three flaws listed in my comment (one of which you correct with an edit). – jfriend00 Mar 05 '16 at 18:56
  • 1
    Including a working snippet would help you find your own issues. It's amazing how many upvotes you got for being clever even though your answer wasn't even close to working at the time. It looks like it has a chance of working now. – jfriend00 Mar 05 '16 at 18:57
  • @jfriend00, agreed: I was putting a JS Fiddle together after first posting the initial version. Then I realised the lexicographic issue which elclanrs also pointed out. – David Thomas Mar 05 '16 at 18:59
1

Why not just sort it (descending order) and take the first two entries

biggest = myArray.sort(function(a,b){return b - a}).slice(0,2);
Suever
  • 64,497
  • 14
  • 82
  • 101
1

The answers above are probably better and more compact, but in case you don't want to use sort() this is another option

function choseBig (myArray) {

    var result = [], firstBig, secondBig;

    // select the biggest value
    firstBig = Math.max.apply(Math, myArray);

    // find its index
    var index = myArray.indexOf(firstBig);

    // remove the biggest value from the original array 
    myArray.splice((index), 1);


    secondBig = Math.max.apply(Math, myArray);

    // push the results into a new array
    result.push(firstBig, secondBig);

    return result;
}

console.log(choseBig ([1,2,3,4,5,9]));
fbid
  • 1,570
  • 2
  • 18
  • 29
1

A linear solution with Array#reduce without sorting.

var array = [1, 2, 3, 4, 5, 9],
    biggest = array.reduce(function (r, a) {
        if (a > r[1]) {
            return [r[1], a];
        }
        if (a > r[0]) {
            return [a, r[1]];
        }
        return r;
    }, [-Number.MAX_VALUE, -Number.MAX_VALUE]);

document.write('<pre>' + JSON.stringify(biggest, 0, 4) + '</pre>');

Edit: more than one biggest value

var array = [1, 2, 3, 4, 5, 9, 9],
    biggest = array.reduce(function (r, a) {
        if (a > r[1]) {
            return [r[1], a];
        }
        if (a > r[0]) {
            return [a, r[1]];
        }
        return r;
    }, [-Number.MAX_VALUE, -Number.MAX_VALUE]);

document.write('<pre>' + JSON.stringify(biggest, 0, 4) + '</pre>');
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • +1 for the linearity.. but you didn't handle the case where the max value occurred more than once. (i.e. `[1, 2, 3, 4, 5, 9, 9]`) – Ahmad Ibrahim Mar 05 '16 at 20:00
  • @AhmadIbrahim, it can handle it. – Nina Scholz Mar 05 '16 at 21:28
  • @AhmadIbrahim, i get [9, 9] on IE11, FF and Chrome, please see edit. – Nina Scholz Mar 06 '16 at 09:04
  • I mean the biggest value is 9, even if it occurred more than once, and the second biggest value is 5, so the result should be [9, 5]. (if I understood the question correctly) – Ahmad Ibrahim Mar 06 '16 at 09:08
  • i understand the question that the two biggest numbers do not have to be different. the approces with sorting returns as well the biggest number even if they are the same, as my answer. – Nina Scholz Mar 06 '16 at 09:14
0

If you want to retrieve the largest two values from a numeric array in a non-destructive fashion (e.g. not changing the original array) and you want to make it extensible so you can ask for the N largest and have them returned in order, you can do this:

function getLargestN(array, n) {
    return array.slice(0).sort(function(a, b) {return b - a;}).slice(0, n);
}

And, here's a working snippet with some test data:

function getLargestN(array, n) {
    return array.slice(0).sort(function(a, b) {return b - a;}).slice(0, n);
}

// run test data
var testData = [
    [5,1,2,3,4,9], 2,
    [1,101,22,202,33,303,44,404], 4,
    [9,8,7,6,5], 2
];

for (var i = 0; i < testData.length; i+=2) {
    if (i !== 0) {
        log('<hr style="width: 50%; margin-left: 0;">');
    }
    log("input: ", testData[i], " :", testData[i+1]);
    log("output: ", getLargestN(testData[i], testData[i+1]));
}
<script src="http://files.the-friend-family.com/log.js"></script>
jfriend00
  • 683,504
  • 96
  • 985
  • 979
0

With Math#max and Array#splice

var first = Math.max(...arr)
arr.splice(arr.indexOf(first))
var second = Math.max(...arr)

and with ES6 spread operator

isvforall
  • 8,768
  • 6
  • 35
  • 50
0

I like the linear solution of Nina Scholz. And here's another version.

function chooseBig (myArray) {
   var a = myArray[0], b = myArray[0];
   for(i = 1; i < myArray.length; i++) {
       if (myArray[i] === a) {
           continue;
       } else if (myArray[i] > a) {
           b = a;
           a = myArray[i];
       } else if (myArray[i] > b || a === b) {
           b= myArray[i];
       }
   }
   return [a, b];
}
Community
  • 1
  • 1
Ahmad Ibrahim
  • 1,915
  • 2
  • 15
  • 32
-2
[1,101,22,202].sort(function(a, b){return b-a})[0]
[1,101,22,202].sort(function(a, b){return b-a})[1]
Roshni Bokade
  • 343
  • 2
  • 9