For some reason I'm having some serious difficulty wrapping my mind around this problem. I need this JS function that accepts 2 arrays, compares the 2, and then returns a string of the missing element. E.g. Find the element that is missing in the currentArray that was there in the previous array.
function findDeselectedItem(CurrentArray, PreviousArray){
var CurrentArrSize = CurrentArray.length;
var PrevousArrSize = PreviousArray.length;
// Then my brain gives up on me...
// I assume you have to use for-loops, but how do you compare them??
return missingElement;
}
Thank in advance! I'm not asking for code, but even just a push in the right direction or a hint might help...
Problem statement:
Find the element that is missing in the currentArray that was there in the previous array.
previousArray.filter(function(x) { // return elements in previousArray matching...
return !currentArray.includes(x); // "this element doesn't exist in currentArray"
})
(This is as bad as writing two nested for-loops, i.e. O(N2) time*). This can be made more efficient if necessary, by creating a temporary object out of currentArray, and using it as a hashtable for O(1) queries. For example:)
var inCurrent={}; currentArray.forEach(function(x){ inCurrent[x]=true });
So then we have a temporary lookup table, e.g.
previousArray = [1,2,3]
currentArray = [2,3];
inCurrent == {2:true, 3:true};
Then the function doesn't need to repeatedly search the currentArray every time which would be an O(N) substep; it can instantly check whether it's in currentArray in O(1) time. Since .filter is called N times, this results in an O(N) rather than O(N2) total time:
previousArray.filter(function(x) {
return !inCurrent[x]
})
Alternatively, here it is for-loop style:
var inCurrent = {};
var removedElements = []
for(let x of currentArray)
inCurrent[x] = true;
for(let x of previousArray)
if(!inCurrent[x])
removedElements.push(x)
//break; // alternatively just break if exactly one missing element
console.log(`the missing elements are ${removedElements}`)
Or just use modern data structures, which make the code much more obvious:
var currentSet = new Set(currentArray);
return previousArray.filter(x => !currentSet.has(x))
*(sidenote: or technically, as I illustrate here in the more general case where >1 element is deselected, O(M*N) time)
This should work. You should also consider the case where the elements of the arrays are actually arrays too. The indexOf might not work as expected then.
function findDeselectedItem(CurrentArray, PreviousArray) {
var CurrentArrSize = CurrentArray.length;
var PreviousArrSize = PreviousArray.length;
// loop through previous array
for(var j = 0; j < PreviousArrSize; j++) {
// look for same thing in new array
if (CurrentArray.indexOf(PreviousArray[j]) == -1)
return PreviousArray[j];
}
return null;
}
Take a look at underscore difference function: http://documentcloud.github.com/underscore/#difference
I know this is code but try to see the difference examples to understand the way:
var current = [1, 2, 3, 4],
prev = [1, 2, 4],
isMatch = false,
missing = null;
var i = 0, y = 0,
lenC = current.length,
lenP = prev.length;
for ( ; i < lenC; i++ ) {
isMatch = false;
for ( y = 0; y < lenP; y++ ) {
if (current[i] == prev[y]) isMatch = true;
}
if ( !isMatch ) missing = current[i]; // Current[i] isn't in prev
}
alert(missing);
Or using ECMAScript 5 indexOf:
var current = [1, 2, 3, 4],
prev = [1, 2, 4],
missing = null;
var i = 0,
lenC = current.length;
for ( ; i < lenC; i++ ) {
if ( prev.indexOf(current[i]) == -1 ) missing = current[i]; // Current[i] isn't in prev
}
alert(missing);
And with while
var current = [1, 2, 3, 4],
prev = [1, 2, 4],
missing = null,
i = current.length;
while(i) {
missing = ( ~prev.indexOf(current[--i]) ) ? missing : current[i];
}
alert(missing);
This is my approach(works for duplicate entries too):-
//here 2nd argument is actually the current array
function(previousArray, currentArray) {
var hashtable=[];
//store occurances of elements in 2nd array in hashtable
for(var i in currentArray){
if(hashtable[currentArray[i]]){
hashtable[currentArray[i]]+=1; //add 1 for duplicate letters
}else{
hashtable[currentArray[i]]=1; //if not present in hashtable assign 1
}
}
for(var i in previousArray){
if(hashtable[previousArray[i]]===0 || hashtable[previousArray[i]] === undefined){ //if entry is 0 or undefined(means element not present)
return previousArray[i]; //returning the missing element
}
else{
hashtable[previousArray[i]]-=1; //reduce count by 1
}
}
}
Logic is that i have created a blank array called hashtable. We iterate currentArray first and use the elements as index and values as counts starting from 1(this helps in situations when there are duplicate entries). Then we iterate through previousArray and look for indexes, if they match we reduce the value count by 1. If an element of 2nd array doesnt exist at all then our undefined check condition fires and we return it. If duplicates exists, they are decremented by 1 each time and when 0 is encountered, that elment is returned as missing element.
Related
I am completing the hackerrank's 10 days of javascript challenge. The question:
write a function to take an array as an argument and then return the second largest element in the array.
I have written the code but my code is returning the largest element and not the second largest as asked.
function getSecondLargest(nums) {
// Complete the function
var largest=nums[0];
for(let i=1;i<nums.length;++i)
{
if(nums[i]>largest)
largest=nums[i];
}
var large=nums[0];
for(let j=1;j<nums.length;++j)
{
if(large<nums[j]&&large<largest)
large=nums[j];
}
return large;
}
When input array nums={2,3,6,6,5} the result is coming 6 while expected output is 5. Please help and point out the errors in the function code below.
should not initialize large with first value var large=nums[0]; because it may appear the biggest value and won't work
should use nums[j]<largest instead of large<largest as mentioned above
I think don't need second loop as all checks can be done in first loop, and you can assign prev largest to large whenever you change it:
function getSecondLargest(nums) {
var largest = nums[0];
var large;
for (let i = 1; i < nums.length; ++i) {
if (nums[i] > largest) {
large = largest;
largest = nums[i];
} else if (nums[i] > large || typeof large === 'undefined') {
large = nums[i]
}
}
return large;
}
console.log(getSecondLargest([5,1-2,3]))
console.log(getSecondLargest([-5,1,-2,3]))
GET SECOND LARGEST
first, I create new array with unique values.
let arr = [...new Set(nums)];
second, sort value using built-in function .sort().
note : by default .sort() always sorts asciibetically, but for some testcase, it doesn't work. So, I put (a, b) => { return a - b } to make sure it will work properly.
arr = arr.sort((a, b) => { return a -b });
third, get the value from arr
let result = arr[arr.length - 2] || arr[0];
finally, return the result
return result
function getSecondLargest(nums) {
let arr = [...new Set(nums)];
//Javascript's array member method .sort( always sorts asciibetically.
arr = arr.sort((a, b) => { return a - b });
let result = arr[arr.length - 2] || arr[0];
return result
}
Just one minor change:
Use nums[j]<largest instead of large<largest in the second for loop
function getSecondLargest(nums) {
// Complete the function
var largest=nums[0];
for(let i=1;i<nums.length;++i)
{
if(nums[i]>largest)
largest=nums[i];
}
var large;
//To ensure that the selected number is not the largest
for(let j=0;j<nums.length;++j)
{
if (nums[j] !== largest){
large = nums[j];
break;
}
}
for(let j=1;j<nums.length;++j)
{
if(large<nums[j]&&nums[j]!=largest)
large=nums[j];
else
console.log(large)
}
return large;
}
var secondLargest = getSecondLargest([6,3,6,6,5]);
console.log("Second largest number", secondLargest);
If you want to avoid using library functions like #ifaruki suggests, this line
if(large<nums[j]&&large<largest)
should read
if (large<nums[j] && nums[j] < largest)
Sorting and picking the second or second-to-last value fails when there are duplicates of the highest value in the input array.
Another easiest logic is to remove duplicates from the array and sort.
let givenArray = [2, 3, 6, 6, 5];
let uniqueArray = [...new Set(givenArray)];
console.log("The second largets element is", uniqueArray.sort()[uniqueArray.length - 2]);
I know you had your question answered, just thought I would provide my solution for any future users looking into this.
You can use reduce to go through the array while remembering the two largest numbers so far.
You just make a simple reduction function:
function twoMax(two_max, candidate)
{
if (candidate > two_max[0]) return [candidate,two_max[0]];
else if (candidate > two_max[1]) return [two_max[0],candidate];
else return two_max;
}
And then you use it for example like this:
let my_array = [0,1,5,7,0,8,12];
let two_largest = my_array.reduce(twoMax,[-Infinity,-Infinity]);
let second_largest = two_largest[1];
This solution doesn't require sorting and goes through the array only once.
If you want to avoid using **sort method. I think here's the easiest logic to do that, which will also work in arrays where there's duplicates of largest integer exists.
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([5, 7, 11, 11, 11])); //7
I would like to cache some data in javascript, but the cache should be limited to 10 elements for example.
I can place the objects in javascript array, but what is the best way to keep the array limited to 10 elements?
Example:
function getData(dataId) { return new NextDataObject(dataId); }
var array = new Array();
array.push(getData(0));
array.push(getData(1));
(...)
array.push(getData(10)); // this should result in dropping "oldest" data, so getData(0) should be removed from the array, so that in array there are only 10 objects at maximum
Should such mechanism be written manually (using splice() for example?) or are there better ways to achieve such "cache" structure in javascript?
BTW: in this particular situation I'm using angular.
Override the push function of your caching array.
var array = new Array()
array.push = function (){
if (this.length >= 10) {
this.shift();
}
return Array.prototype.push.apply(this,arguments);
}
Plunker
To make this more reusable I created a method which returns new instance of such array (basing on above code).
function getArrayWithLimitedLength(length) {
var array = new Array();
array.push = function () {
if (this.length >= length) {
this.shift();
}
return Array.prototype.push.apply(this,arguments);
}
return array;
}
var array = getArrayWithLimitedLength(10);
To remove first element from array use shift:
if (arr.length > 10) {
arr.shift(); // removes the first element from an array
}
How about this object?
function Cache(maxLength) {
this.values = [];
this.store = function(data) {
if(this.values.length >= maxLength) {
this.getLast();
}
return this.values.push(data);
}
this.getLast = function() {
return this.values.splice(0,1)[0];
}
}
cache = new Cache(3);
// => Cache {values: Array[0]}
cache.store(1)
// => 1
cache.store(2)
// =>2
cache.store(3)
// => 3
cache.store(4)
// =>3
cache.values
// => [2, 3, 4]
cache.getLast()
// => 2
cache.values
[3, 4]
You could create new method in Array.prototype to mimic your needs.
Array.prototype.push_with_limit = function(element, limit){
var limit = limit || 10;
var length = this.length;
if( length == limit ){
this.shift();
}
this.push(element);
}
var arr = []
arr.push_with_limit(4); // [4]
arr.push_with_limit(9); // [4, 9]
....
// 11th element
arr.push_with_limit(3); // [9, ..., 3] 10 elements
Simple fixed length queue:
Array.prototype.qpush = function( vals, fixed ) {
if (arguments.length) {
if (Array.isArray(vals)) {
for (var v of vals) {
this.push(v);
}
} else {
this.push(vals);
}
var _f = (typeof this.fixed != undefined) ? this.fixed : 0;
if (typeof fixed != undefined) {
_f = (Number(fixed)===fixed && fixed%1===0 ) ? fixed : _f;
}
this.fixed = _f;
if (this.fixed>0) this.splice(0, this.length - _f);
}
}
var q = new Array();
q.push(0);
q.qpush( [1, 2, 3], 10 );
q.qpush( [4] );
q.qpush( 5 );
q.qpush( [6, 7, 8, 9, 10, {k:"object"} ] );
console.log(q);
if(array.length == 10) {
array.splice(0, 1);
// this will delete first element in array
}
If you do a check whether the array has reached 10 entries with array.length, just remove the first element before pushing a new element. This can be done several ways as Tushar states, array.shift() would be the fastest, but you can indeed use array.splice() aswell.
It would look like this:
if(array.length > 10) {
array.shift();
array.push(getData(10));
}
On a side note, instead of using var array = new Array() I suggest you simply use var array = [];. This is because the new keyword in Javascript sometimes has bad side effects. If you for example want to create an array with 1 element being a digit, and you use var arr = new Array(12);, an array with 12 undefined elements will be created. Whereas var arr = [12]; will create an array with 1 element, the digit 12.
But I guess that's a minor thing to consider..
You could use an object instead...
var obj = {}; //your cache object
obj[window.performance.now()] = getData(val); //add value, index by microsecond timestamp
if(Object.keys(obj).length > 10){ // then if the length ever gets bigger than 10..
var array = Object.keys(obj).sort(); //sort the properties by microsecond asc
delete obj[array[0]]; //delete the oldest one
}
Here is a jsFiddle example showing how it works: https://jsfiddle.net/uhkvk4mw/
just check if the length is reached then pop it
if(arr.length > someNumber){
arr.pop(); // pop() will remove the last element
}
I am having trouble checking the contents of an array contained within a main array.
Example:
I have two arrays
var main = [[1,2,3],
[4,5,6]];
var compare = [1,2,4,5,6]
I want to compare the array "compare" with each array within the array "main" to see if it contains any of the numbers. The result would be something I could then test against (boolean or the index position).
I tried indexOf and couldn't figure it out.
Edit
This should still return true:
var main = [[1,2,3], // returns false
[4,5,6], // returns false
[7,8,9], // returns true
[2,3,7]]; // returns true
var compare = [2,3,4,6,7,8,9]
** Update w/ Solution ***
I needed to check if compare array's contents matched any of the subarrays in main. Here's what I came up with:
var main = [[1, 2, 3],
[4,5,6]];
var counter = 0;
var counter2 = 0;
var compare = [4,1,3,2];
for (var i = 0; i <= compare.length; i++) {
// Sorting
compare.sort();
if (main[0].indexOf(compare[i]) > -1) {
counter++;
console.log("Added a point to counter 1");
} else if (main[1].indexOf(compare[i]) > -1) {
counter2++;
console.log("Added a point to counter 2");
} else {
console.log("No points added");
}
}
// if any of the counters have 3 marks, then the player hit it 3 times.
if (counter == 3 || counter2 === 3){
console.log("A counter is at 3");
}
Any feedback on what I came up with? What's a better way of doing this?
You'll need 2 loops, the first to iterate over your array of arrays, the next to check for existing elements within the current array:
for (var i = 0; i < main.length; i++) {
for (var j = 0; j < main[i].length; j++) {
if (compare.indexOf(main[i][j]) {
//compare has a number from the current array! main[i][j] exists in compare!
}
}
}
Take a look at lodash library, the have that exact functionality written already
You can use built-in array methods:
var result = main.map(function(xs) {
return xs.some(function(x) {
return compare.indexOf(x) > -1
})
})
It will return [true, true]
Following, two of the possible solutions:
Suppose:
var main = [[1,2,3],
[4,5,6],
[7,8,9],
[2,3,7],
[5,1,10]];
var compare = [2,3,4,6,7,8,9];
First solution: return true if any element of the main inner array is included in the master one which is compare:
var result1= main.map(function(element,index,array){
return element.reduce(function(previousValue, currentValue, index, array){
return (previousValue || (compare.indexOf(currentValue) >= 0));
}, false);
});
This solution gives the following result:
result1 = [true,true,true,true,false]
Second solution: return the index of the main inner array elements in compare:
var result2= main.map(function(element,index,array){
return element.map(function(element,index,array){
return (compare.indexOf(element));
});
});
This solution gives the following result:
result2 = [[-1,0,1],[2,-1,3],[4,5,6],[0,1,4],[-1,-1,-1]]
Check this link jsfiddle to see a working example.
Hope it's useful!
How can i handle multiple input values to one output value?
function c(input,val){
return input.indexOf(val)>-1;
}
function result(i){
if(c(i,1) && c(i,2) && c(i,3)){
return "alpha";
}else if(c(i,1) && c(i,2) && !c(i,3)){
return "beta";
}else if(c(i,1) && !c(i,2) && c(i,3)){
return "gamma";
}else if(c(i,1) && !c(i,2)){
return "delta";
}else if(c(i,3)){
return "theta";
}
//..... and so on covering all possible combinations
return null;
}
result([1,2,3]); //output : alpha
result([1,3,2]); //output : alpha
result([1,3,1,1]); //output : gamma
result([1,2,4]); //output : beta
result([3]); //output : theta
Number of values in array can be N, but from a predefined set of values only
What is the right way to work with so many combinations?
Based on Condor's answer, the following should do the job. It uses a sequence of tests to work out the result and also implements the "not" tests. If a value is "true" it must appear, if a value is "false" it must not appear. If it isn't mentioned, it doesn't matter if it's in the values or not.
Duplicates aren't an issue, values are processed until one fails. Removing duplicates first might speed it up a bit though.
The result is the name of the first set of tests that pass.
function testValues(values) {
var checks = [
{alpha: {1:true, 2:true, 3:true }},
{beta : {1:true, 2:true, 3:false}},
{gamma: {1:true, 2:false, 3:true }},
{theta: {3:true}}
];
var check, resultName, tests, passed;
// Do checks in sequence
for (var i=0, iLen=checks.length; i<iLen; i++) {
check = checks[i]
// Get name of result to return if the checks pass
for (resultName in check) {
// Make sure result is own property
if (check.hasOwnProperty(resultName)) {
// Passed is true until a fail is found
passed = true;
// Get tests to perform
tests = check[resultName];
// For each value in tests, make sure value exists or doesn't in values
for (var v in tests) {
if (tests.hasOwnProperty(v)) {
// Only test if passed is true
if (passed) {
// Note that indexOf uses === so must have same type
// Property names are always strings so if passing numbers,
// Must convert to numbers
passed = tests[v] === (values.indexOf(+v) != -1);
}
}
}
// If passed tests, return
if (passed) return resultName;
}
}
}
return 'failed all tests...';
}
console.log(testValues([1,2,3])); //output : alpha
console.log(testValues([1,3,2])); //output : alpha
console.log(testValues([1,3,1,1])); //output : gamma
console.log(testValues([1,2,4])); //output : beta
console.log(testValues([3])); //output : theta
The code could be a bit shorter if Object.keys is used with forEach, but the above is a bit clearer (perhaps), it can be refactored to be shorter. The above will work in ECMA-262 ed 3 environments if a shim for Array.prototype.indexOf is provided.
Edit
If modern features are used, the code can be simplified a bit. Providing support for older browsers isn't difficult:
// These are sufficient to support the function but are not suitable for general use
// Better versions are avaialble at MDN, see links below
if (!Object.keys) {
Object.keys = function(obj) {
var keys = [];
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
keys.push(key);
}
}
return keys;
};
}
if (!Array.prototype.indeOf) {
Array.prototype.indexOf = function (value) {
for (var i=0, iLen=this.length; i<iLen; i++) {
if (this[i] === value) return i;
}
return -1;
};
}
function testValues(values) {
var checks = [
{alpha: {1:true, 2:true, 3:true }},
{beta : {1:true, 2:true, 3:false}},
{gamma: {1:true, 2:false, 3:true }},
{theta: {3:true}}
];
var check, passed, resultName, tests, testKeys;
for (var i=0, iLen=checks.length; i<iLen; i++) {
check = checks[i]
resultName = Object.keys(check)[0];
passed = true;
tests = check[resultName];
testKeys = Object.keys(tests);
for (var j=0, jLen=testKeys.length; j<jLen && passed; j++) {
passed = tests[testKeys[j]] === (values.indexOf(+testKeys[j]) != -1);
}
if (passed) return resultName;
}
return 'failed all tests...';
}
Object.keys: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
Array.prototype.indexOf: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf
The code could perhaps be organized more clearly if the cases are easier accessible. For instance, the alpha case could be formualted by an array [1,2,3] which would list the numbers on which the code depend and and array of boolean values [true,true,true] which would indicate whether the corresponding number occurs or not. The checking could be implemented with a function as follows.
function check_case(values,bools){
var Result = true;
for ( var i = 0; i < values.length; i++ ){
Result = Result && ( c(values[i]) == bools[i] );
}
return Result;
}
This circumvents the formulation of conditions in which results of c have to be negated individually, which makes them difficult to follow and edit.
The idea could be taken a step further by having an array of cases which would also hold the names of the cases. An array containing the first two cases above would then be as follows.
[
{
values: [1,2,3],
bools: [true,true,true],
name: 'alpha'
},
{
values: [1,2,3],
bools: [true,true,false],
name: 'beta'
}
]
The code would then iterate over this array, calling check_case for every index and returning the value of name for the first hit.
Let your predefined array be:- [3,2,1] (i am keeping this short for simplicity, this can extend to N elements)
predefined array as boolean string can be visualized as:- 123
You can treat this number as boolean and create your desired output mapping:-
Example:- If no number :- 000 0
If only 1 is present:- 100 4
If only 1 and 2 are present :- 110 6
and so on all the required boolean combinations can be defined
So for N numbers you can create a list of all the possible combinations you want
Example:- var definedSet = {"0":"Alpha","1":"beta","2":"gama","3":"x","4":"y","5":"z","6":"a","7":"b"};
Now take input and remove duplicates and check the location (bit location of the number and create a boolean value and map this to defined set)
Code:-
function sortAndRemoveDuplicates(arr) {
arr.sort( function(a, b) { return a - b; } );
var copy = arr.slice(0);
arr.length = 0;
for (var i = 0, len = copy.length; i < len; ++i) {
if (i == 0 || copy[i] != copy[i - 1]) {
arr.push(copy[i]);
}
}
return arr;
}
var definedArr = [3,2,1];
var definedSet = {"0":"Alpha","1":"beta","2":"gama","3":"x","4":"y","5":"z","6":"a","7":"b"};
var inputArr=[1,4,3,1,1];
sortAndRemoveDuplicates(inputArr);
var outBooleanValue = 0;
for(var i=0;i<inputArr.length;i++)
{
var numIndex =definedArr.indexOf(inputArr[i]);
if(numIndex!=-1)
{
outBooleanValue+=Math.pow(2,numIndex);
}
}
result =definedSet[outBooleanValue.toString()];
alert(result);
Here is the working fiddle:-
http://jsfiddle.net/9tFnn/1/
Here is another version of this where predefined inputs are not required and it allows you to give conditions as you specified:-
var definedSet = {"1":"alpha","12":"beta","12!3":"gama","123":"delta"};
$("#defined-set").html(JSON.stringify(definedSet));
var inputArr=[1,2];
$("#input").html(JSON.stringify(inputArr));
$.unique(inputArr);
inputArr.sort();
var outputStr = inputArr.join('');
var regexStr = '';
var loopCounter=0;
for(loopCounter=0;loopCounter<outputStr.length;loopCounter++)
regexStr+='(!\\d)*'+outputStr[loopCounter]+'(!\\d)*';
regexStr+='$';
//var regexPattern = new RegExp(regexStr);
console.log(regexStr);
for(var currSet in definedSet)
{
//var match = regexPattern.test(currSet);
var match = currSet.search(regexStr);
if(match!=-1)
{
if(currSet==outputStr)
{
$("#result").append("Exact Match::");
}
$("#result").append(currSet+"<br/>");
}
}
Here is the link to working fiddle:-
http://jsfiddle.net/hLUuF/1/
Note:- This is just a basic prototype of the algorithm used to get to the answer, this can be modified according to the programming needs.
Hope this proves to be useful.
This solution assumes that the order of elements in the input is not important. That is, an input of [1, 3, 2] is the same as an input of [1, 2, 3].
It looks like what you're trying to do is take a set of possible inputs from a range (1..n), each of which may or may not be present. In other words, for a range of [1, 2, 3, 4], one possible input would be [1, 3]. Or,
Out of: [1, 2, 3, 4]
We want: [x, 0, x, 0]
Which looks suspiciously like a binary number, especially if we reverse the order. So all we really need to do is
Convert our input to a binary number
Use that binary number as an index into a predefined look-up table of values
For the first part all we need to do is loop through the input values and OR the appropriate bits together. This also automatically takes care of any duplicates.
for (i=0;i<input_array.length;i++)
{
num = num | (1 << (input_array[i] - 1));
}
The - 1 in there is because our sequence starts from 1 rather than 0. So an input of 4 will result in 1000 and an input of 2 will result in 0010. OR-ing them together gives us 1010 or 10 (decimal).
Now num contains the index into our table which we set up beforehand:
values[0] = "alpha"; // Corresponding to input []
values[1] = "beta"; // Corresponding to input [1]
...
values[10] = "gamma"; // Corresponding to input [4, 2] - remember, it's reversed
...
values[15] = "theta"; // Corresponding to input [4, 3, 2, 1]
I have this:
var arr = [0, 21, 22, 7];
What's the best way to return the index of the highest value into another variable?
This is probably the best way, since it’s reliable and works on old browsers:
function indexOfMax(arr) {
if (arr.length === 0) {
return -1;
}
var max = arr[0];
var maxIndex = 0;
for (var i = 1; i < arr.length; i++) {
if (arr[i] > max) {
maxIndex = i;
max = arr[i];
}
}
return maxIndex;
}
There’s also this one-liner:
let i = arr.indexOf(Math.max(...arr));
It performs twice as many comparisons as necessary and will throw a RangeError on large arrays, though. I’d stick to the function.
In one line and probably faster then arr.indexOf(Math.max.apply(Math, arr)):
var a = [0, 21, 22, 7];
var indexOfMaxValue = a.reduce((iMax, x, i, arr) => x > arr[iMax] ? i : iMax, 0);
document.write("indexOfMaxValue = " + indexOfMaxValue); // prints "indexOfMaxValue = 2"
Where:
iMax - the best index so far (the index of the max element so far, on the first iteration iMax = 0 because the second argument to reduce() is 0, we can't omit the second argument to reduce() in our case)
x - the currently tested element from the array
i - the currently tested index
arr - our array ([0, 21, 22, 7])
About the reduce() method (from "JavaScript: The Definitive Guide" by David Flanagan):
reduce() takes two arguments. The first is the function that performs the reduction operation. The task of this reduction function is to somehow combine or reduce two values into a single value, and to return that reduced value.
Functions used with reduce() are different than the functions used with forEach() and map(). The familiar value, index, and array values are passed as the second, third, and fourth arguments. The first argument is the accumulated result of the reduction so far. On the first call to the function, this first argument is the initial value you passed as the
second argument to reduce(). On subsequent calls, it is the value returned by the previous invocation of the function.
When you invoke reduce() with no initial value, it uses the first element of the array as the initial value. This means that the first call to the reduction function will have the first and second array elements as its
first and second arguments.
Another solution of max using reduce:
[1,2,5,0,4].reduce((a,b,i) => a[0] < b ? [b,i] : a, [Number.MIN_VALUE,-1])
//[5,2]
This returns [5e-324, -1] if the array is empty. If you want just the index, put [1] after.
Min via (Change to > and MAX_VALUE):
[1,2,5,0,4].reduce((a,b,i) => a[0] > b ? [b,i] : a, [Number.MAX_VALUE,-1])
//[0, 3]
To complete the work of #VFDan, I benchmarked the 3 methods: the accepted one (custom loop), reduce, and find(max(arr)) on an array of 10000 floats.
Results on chromimum 85 linux (higher is better):
custom loop: 100%
reduce: 94.36%
indexOf(max): 70%
Results on firefox 80 linux (higher is better):
custom loop: 100%
reduce: 96.39%
indexOf(max): 31.16%
Conclusion:
If you need your code to run fast, don't use indexOf(max).
reduce is ok but use the custom loop if you need the best performances.
You can run this benchmark on other browser using this link:
https://jsben.ch/wkd4c
If you are utilizing underscore, you can use this nice short one-liner:
_.indexOf(arr, _.max(arr))
It will first find the value of the largest item in the array, in this case 22. Then it will return the index of where 22 is within the array, in this case 2.
Unless I'm mistaken, I'd say it's to write your own function.
function findIndexOfGreatest(array) {
var greatest;
var indexOfGreatest;
for (var i = 0; i < array.length; i++) {
if (!greatest || array[i] > greatest) {
greatest = array[i];
indexOfGreatest = i;
}
}
return indexOfGreatest;
}
var arr=[0,6,7,7,7];
var largest=[0];
//find the largest num;
for(var i=0;i<arr.length;i++){
var comp=(arr[i]-largest[0])>0;
if(comp){
largest =[];
largest.push(arr[i]);
}
}
alert(largest )//7
//find the index of 'arr'
var arrIndex=[];
for(var i=0;i<arr.length;i++){
var comp=arr[i]-largest[0]==0;
if(comp){
arrIndex.push(i);
}
}
alert(arrIndex);//[2,3,4]
EDIT: Years ago I gave an answer to this that was gross, too specific, and too complicated. So I'm editing it. I favor the functional answers above for their neat factor but not their readability; but if I were more familiar with javascript then I might like them for that, too.
Pseudo code:
Track index that contains largest value. Assume index 0 is largest initially. Compare against current index. Update index with largest value if necessary.
Code:
var mountains = [3, 1, 5, 9, 4];
function largestIndex(array){
var counter = 1;
var max = 0;
for(counter; counter < array.length; counter++){
if(array[max] < array[counter]){
max = counter;
}
}
return max;
}
console.log("index with largest value is: " +largestIndex(mountains));
// index with largest value is: 3
function findIndicesOf(haystack, needle)
{
var indices = [];
var j = 0;
for (var i = 0; i < haystack.length; ++i) {
if (haystack[i] == needle)
indices[j++] = i;
}
return indices;
}
pass array to haystack and Math.max(...array) to needle. This will give all max elements of the array, and it is more extensible (for example, you also need to find min values)
If you create a copy of the array and sort it descending, the first element of the copy will be the largest. Than you can find its index in the original array.
var sorted = [...arr].sort((a,b) => b - a)
arr.indexOf(sorted[0])
Time complexity is O(n) for the copy, O(n*log(n)) for sorting and O(n) for the indexOf.
If you need to do it faster, Ry's answer is O(n).
A minor modification revised from the "reduce" version of #traxium 's solution taking the empty array into consideration:
function indexOfMaxElement(array) {
return array.reduce((iMax, x, i, arr) =>
arr[iMax] === undefined ? i :
x > arr[iMax] ? i : iMax
, -1 // return -1 if empty
);
}
A stable version of this function looks like this:
// not defined for empty array
function max_index(elements) {
var i = 1;
var mi = 0;
while (i < elements.length) {
if (!(elements[i] < elements[mi]))
mi = i;
i += 1;
}
return mi;
}
To find the index of the greatest value in an array, copy the original array into the new array and then sort the original array in decreasing order to get the output [22, 21, 7, 0]; now find the value 22 index in the copyNumbers array using this code copyNumbers.indexOf(numbers[0]);
<script>
const numbers = [0, 21, 22, 7];
const copyNumbers = [];
copyNumbers.push(...numbers);
numbers.sort(function(a, b){
return b - a
});
const index = copyNumbers.indexOf(numbers[0]);
console.log(index);
</script>
Make this
const max = arr.reduce((m, n) => Math.max(m, n)), then the indexes of the max
get index with findIndex
var index = arr.findIndex(i => i === max)