I need a function that would return a value from an array that may contain any number of arrays. It should be called like getValueFromArray(array, [2, 4]) - this example should return 4th element of the 2d array of the passed array.
Here is my code:
function getValueFromArray(arr, indexes){
var val,
currentIndex = indexes[0];
if(!arr[currentIndex] || arr[currentIndex] === '') return value = '';
indexes.splice(0, 1);
if(arr[currentIndex].length)
getValueFromArray(arr[currentIndex], indexes);
else {
val = arr[currentIndex];
return val;
}
}
var y = getValueFromArray([[1,2,3,4], [1,2,3,4]], [0, 2]); // should return 3
var x = getValueFromArray([[1,2,3,4], [1,2,3,4], [5,6,7,8]], [2, 3]); // should return 8
var z = getValueFromArray(
[
[[1,2,3,4], [1,2,3,4], [1,2,3,4]],
[[1,2,3,4], [1,2,3,4]]
],
[0, 1, 2]
); // should return 3
Such call should return 3, and if I debug the function, it actually returns the correct value but when I assign it to a variable, it returns undefined. I guess it's because of the recursion, variable gets the value that is undefined during the first function call. How can this be fixed?
Thank you!
You are not returning your recursive results.
if(arr[currentIndex].length)
getValueFromArray(arr[currentIndex], indexes);
Should be:
if(arr[currentIndex].length)
return getValueFromArray(arr[currentIndex], indexes);
you missed one thing in your code(returning the result after if condition) try below given code:-
function getValueFromArray(arr, indexes){
var val,
currentIndex = indexes[0];
if(!arr[currentIndex] || arr[currentIndex] === '') return value = '';
indexes.splice(0, 1);
if(arr[currentIndex].length)
return getValueFromArray(arr[currentIndex], indexes);
else {
val = arr[currentIndex];
return val;
}
}
var y = getValueFromArray([[1,2,3,4], [1,2,3,4]], [0, 2]);
console.log(y)
run it and see, now its showing the result into the variable.
I think you need to add a return before
getValueFromArray(arr[currentIndex], indexes);
To make the final calculated value to recurse up the recursive method call stack as each recursed call returns.
Your guess is correct. You simply forgot to return whatever the recursive call returns:
if(arr[currentIndex].length)
getValueFromArray(arr[currentIndex], indexes); // <---- here
That being said, I have to agree that you could easily do it more concisely (this will eventually destroy indexes, however):
function getValueFromArray(arr, indexes){
while(indexes.length) arr=arr[indexes.shift()]
return arr
}
It's because if condition is not returning any value.
Try following code
function getValueFromArray(arr, indexes){
var val='',currentIndex = indexes[0];
if(!arr[currentIndex] || arr[currentIndex] === '') return val;
indexes.splice(0, 1);
if(arr[currentIndex].length) {
// Because if your function walks this way
// it does not meet any 'return' statement
// till the end and returns nothing.
return getValueFromArray(arr[currentIndex], indexes);
}
else {
val = arr[currentIndex];
return val;
}
}
Then console log you variable
var y = getValueFromArray([[1,2,3,4], [1,2,3,4]], [0, 2]);
console.log(y)
I'll post my code too as I feel it provides a simpler solution to your problem.
There is no recursion involved, so it should run a bit faster in theory.
var arr = [
[
[
[12, 5, 6],
[ 6, 7, 8],
[11, 0, 9]
],
[
[-1, 1, 8],
[ 4, 5, 6]
]
],
[
[
[7, 8, 9, 10]
]
]
];
function getValueFromArray(arr, indexes){
var value = arr, i = 0, len = indexes.length, index = null;
for (; i < len; i += 1) {
index = indexes[i];
// check if the current array contains an {index}-th record
if ( index in value ) {
value = value[index];
} else {
// or throw an exception if you want
return null;
}
}
return value;
}
getValueFromArray(arr, [0, 1, 1, 2]) // 6
function getValueFromArray(arr, indexes) {
// exit if arr is not an array
// exit if arr is empty
// exit if indexes is not an array
// exit if indexes is empty
if (!Array.isArray(arr) || !arr.length || !Array.isArray(indexes) || !indexes.length) {
return; // may throw exception or return other value
}
var currentIndex = indexes.shift();
// exit if index is not in arr
// exit if index is negative
if (arr.length <= currentIndex || currentIndex < 0) {
return; // may throw exception or return other value
}
return Array.isArray(arr[currentIndex]) ? getValueFromArray(arr[currentIndex], indexes) : arr[currentIndex];
}
You are overthinking this:
function getValueFromArray(arr, indexes){
return arr[indexes[0]][indexes[1]];
}
EDIT for 1 to 3 dimensional array:
function getValueFromArray(arr, indexes){
if (indexes.length == 1) {
return arr[indexes[0]];
} else if (indexes.length == 2) {
return arr[indexes[0][indexes[1]];
} else if (indexes.length == 3) {
return arr[indexes[0][indexes[1][indexes[2]];
} else {
// 4 dimensional arrays???
}
}
Would you have more than 3 dimensional array?
It would be best find a way to append indexes[i] but I can't think of a way at the moment and I don't think it's possible.
Related
var myArr = [1,2,3,4,5,6,7,8,9,10];
function even(num) {
var newArr = [];
for (var i=1; i<num.length; i++) {
if (num[i] % 2 === 0) {
newArr.push(num[i]);
}
}
return newArr;
}
console.log(even(myArr));
My function throws an exception when called. How can I rewrite or refactor the above code to return the first 5 positive numbers?
You can create it this way.
var myArr = [1,2,0,3,4,-6,5,-3,88,21,-6,5,6,7,8,9,10];
let evens = myArr.filter(x => x > 0 && x % 2 == 0).slice(0, 5);
console.log(evens)
First off, your code appears to work. Can you give an example of the error that is occurring? Second off, if you want a function that returns an array of the first n positive, even integers, you can write something like this.
function firstEven(count) {
var response = []; // Create the response list
for(var i=0;i<count;i++) { // Loop for number of even numbers you want
response.push((i + 1) * 2); // *2 skips every two numbers, and +1 shifts the number to even
}
return response
}
However, if you want to just filter out all odd numbers from an array, you can do the following.
var myArr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var myEvens = myArr.filter(function(myNum) { // Filter runs the function for every value in the array, and (if the function returns false) it removed that value
return (myNum % 2) == 0;
});
Feel free to ask if you have any questions!
2 others differnts ways
const myArr = [1,2,3,4,-6,5,-3,88,21,-6,5,6,7,8,9,10];
const even_1 = arr => arr.filter(x=>(x>=0 && !(x&1))).slice(0,5)
const even_2 = arr =>
{
let r = []
for(x of arr)
if (x>=0 && !(x&1)) // test2 = boolean AND on bit zero
{
r.push(x);
if (r.length >= 5) break;
}
return r
}
console.log('even 1:', even_1(myArr).join(','))
console.log('even 2:', even_2(myArr).join(','))
One suggestion:
var myArr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
function even(numbersArray) {
var first5EvenNums = [];
for (const num of numbersArray) {
if (first5EvenNums.length >= 5) break;
if (num % 2 === 0) {
first5EvenNums.push(num);
}
}
return first5EvenNums;
}
console.log(even(myArr));
<script src="//ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<div id="test"></div>
<script>
var myArr = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
function even(num){
var newArr = [];
for (var i=1; i<num.length; i++){
if (num[i] % 2 === 0)
{
newArr.push(num[i]);
if(newArr.length == 5){
return newArr;
}
}
}
return newArr;
};
$("#test").text(even(myArr));
</script>
This return first 5 positive number in array.
I wish to execute a program with output as :
console.log(range(1, 10));
// → [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
console.log(range(5, 2, -1));
// → [5, 4, 3, 2]
console.log(sum(range(1, 10)));
// → 55
I am getting an error for array.length.Please find the code below:
var array = [];
function range (arr){
var lower = Math.min(arr[0],arr[1]);
var upper = Math.max(arr[0],arr[1]);
for (var i=lower;i<=upper;i++){
array.push(i);
}
}
function sum(array){
for(var i=0;i < array.length;i++){
var total = total+array[i];
}
}
console.log(sum(range(1, 10)));
I am at begineers level, please do help.
Thanks.
You have a few problems here:
1.) You aren't returning anything in your range function. You need to return the filled array.
2.) You aren't passing the array correctly in the sum function call.
3.) You aren't returning anything in your sum function call.
Without returning any values, you aren't letting your code blocks work with eachother
var array = [];
function range (arr){
var lower = Math.min(arr[0],arr[1]);
var upper = Math.max(arr[0],arr[1]);
for (var i=lower;i<=upper;i++){
array.push(i);
}
return array; // return the array to be used in the sum function
}
function sum(array){
var total = 0; // need to create a variable outside the loop scope
for(var i in array){
total = total+array[i];
}
return total;
}
console.log(sum(range([1,10]))); // pass the array correctly
Note that you need to set the total variable outside the scope of the for-loop within the sum function. That way you can return the final value. Otherwise, it would return undefined.
See the fiddle: https://jsfiddle.net/udyhb95a/
You need to pass an array when calling the range function you defined range([1, 10])
You need to rewrite your sum function
As a side note, there are more efficient ways to compute the sum of a range of elements without iterating on them.
function sum_of_range(start, end) {
return end * (end + 1) / 2 - start * (start + 1) / 2;
}
Edit:
Here is a working sum function
function sum(array) {
var accumulator = 0;
for (var i = 0; i < array.length; ++i)
accumulator += array[i];
return accumulator;
}
Here you declare a function with one parameter as an array
function range (arr){
...
But here you call a function with two arguments as numbers
console.log(range(1, 10));
Use this call function
console.log(range([1, 10]));
And don't use for..in for arrays
for(var i in array){ it doesn't work as you expect
Use forEach function or plan for loop
Also you have some error in sum function
See working example below:
function range(arr) {
var array = [];
var lower = Math.min(arr[0], arr[1]);
var upper = Math.max(arr[0], arr[1]);
for (var i = lower; i <= upper; i++) {
array.push(i);
}
return array;
}
function sum(array) {
var total = 0;
for (var i = 0; i < array.length; i++) {
total = total + array[i];
}
return total;
}
document.write('range ' + range([1, 10]) + '<br>');
document.write('sum ' + sum(range([1, 10])));
You need to modify sum & range function
function range (){
var array = [];
var lower = Math.min.apply(null, arguments);
var upper = Math.max.apply(null, arguments);
for (var i=lower;i<=upper;i++){
array.push(i);
}
return array;
}
function sum(array){
return array.reduce((x,y)=>x+y,0);
}
console.log(range(1, 10));
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
console.log(range(5, 2, -1)); //if we are considering min & max from params
// [-1, 0, 1, 2, 3, 4, 5]
console.log(sum(range(1, 10)));
// 55
Hello Dear check it now.
var array = [];
function range(arr, arr1) {
var lower = Math.min(arr);
var upper = Math.max(arr1);
for (var i = lower; i <= upper; i++) {
array.push(i);
}
}
function sum() {
var total = 0;
for (var i = 0; i < array.length; i++) {
total = total + array[i];
}
return total;
}
console.log(sum(range(1, 10)));
This is the correct answer to the problem at the end of the data structures chapter within Eloquent JavaScript
function range(start, end, step) {
let arr = []; //declare an empty array
var step = step || 1;//tests to see if step was supplied, otherwise it's 1
if(start < end)
{
for(let i = start; i <= end; i += step)
{
arr.push(i);
}
}
else
{
for(let i = start; i >= end; i += step)
{
arr.push(i);
}
}
return arr;
}
function sum(array) {
let total = 0;
for(let i = 0; i < array.length; i++)
{
total += array[i];
}
return total;
}
console.log(range(1, 10));
// → [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
console.log(range(5, 2, -1));
// → [5, 4, 3, 2]
console.log(sum(range(1,10)));
// → 55
This solution takes into account of entering a step that isn't expected to be positive/negative, i.e range from 1 to 5, we would expect step to be positive, but if the user somehow entered a negative step then an empty array would occur.
The browser actually hangs for the opposite, if the array is expected to decrease, but the step sizes are > 0.
'use strict';
function range(start, end, step = 1){
let output = [];
if (start > end){
// Sanity check, all steps if expected to go down should be negative
if (step > 0){
step = -step;
}
for (;start >= end; start += step){
console.log(start);
output.push(start);
}
}
else{
// Likewise, as all steps should be positive
if (step < 0){
step = -step;
}
for (;start <= end; start += step){
output.push(start);
}
}
return output;
}
function sum(arr){
let output = 0;
for (let i of arr){
output += i;
}
return output;
}
console.log(range(1, 5, 1));
// → [1, 2, 3, 4, 5]
console.log(range(5, 1, -1));
// → [5, 4, 3, 2, 1]
// Notice this one step is a positive, but this is handled (original solution returned empty array)
console.log(range(5, 1, 1));
// → [5, 4, 3, 2, 1]
console.log(sum(range(1,10)));
// → 55
An improvement onto this is to use the reduce function for an array to sum instead of a for loop, i.e:
function sum(array){
return array.reduce((x,y)=>x+y,0);
}
For people finding this later on as I did, here is a way to write the range function so you can pass the input as written in the original question:
console.log(sum(range(1, 10)));
…and a cleaned up sum function similar to the one in A. Sharma's answer:
function range(lower, upper) {
let array = []
for (let i = lower; i <= upper; i++) {
array.push(i);
}
return array;
}
function sum(array) {
let total = 0;
for (let i in array) {
total = total + array[i];
}
return total;
}
console.log(sum(range(1, 10)));
Also worth mentioning:
The use of reduce in JagsSparrow's answer, which is elegant, while not entirely obvious and newcomer friendly as Mathias Vonende pointed out.
Negative step tolerant versions in answers from Jimmy Wei and user3225968.
This is the best solution I've got
function range(x,y){
var arr = [];
for(x;x<=y;x++){
arr.push(x);
};
return arr;
};
function sum(array){
return array.reduce((a,b) => a + b, 0);
};
console.log(sum(range(1,10)));
This answer is quite late but I am learning these things now and want to share my solution. I have not seen this solution provided for the specific question "Sum of a range in Javascript" so I wanted to share it. What I have done here is made use of the pop method for the array which allowed me not to specifically pass an array argument to the range function but to provide a solution to the argument in the way it was originally presented in the question.
var result = [];
var counter = 0;
function range(start, end) {
for (let i = start; i <= end; i++) {
result.push(i);
}
return result;
}
function sum(array) {
for (let i = 0; i < result.length; i++) {
counter += result.pop(i);
}
return counter;
}
console.log(range(1, 10));
// → [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
console.log(sum(range(1, 10)));
// → 55
This can be accomplished very easily and efficiently without any globally scoped vars.
It's not clear in the original question what behavior should be applied to the -1 argument. It seems to be an indicator to reverse the range. In the below example, I've used a boolean to check this argument. A value of -1 would actually be the same as not providing a third argument. To reverse the range, pass in any truthy value.
function range(from, to, reverse) {
// Make sure our inputs are actually numbers
if (Number(from) != from || Number(to) != to) {
throw new TypeError("range() expects a Number as both it's first and second argument");
}
let o = []; // initialize our output array
// get the lowest value argument as our starting index
let i = Math.min(from, to);
// get the highest value argument as our ending index
let x = Math.max(from, to);
// push i onto our output array and then increment until i == x
while (i <= x) { o.push(i); i++; }
// reverse the range order if necessary
if (reverse) { o = o.reverse(); }
// return our output array
return o;
}
Then we can use Array.reduce to iterate through the range array and add each value (b) to the one before it (a) with the addition assignment operator (+=).
function sum(range) {
if (!(range instanceof Array)) {
throw new TypeError("sum() expects an Array as it's only argument");
} return range.reduce((a,b) => a+=b);
}
Testing it:
let a = range(1,10);
let b = range(5,2);
let c = range(5,2,true);
let d = range(3,-1);
let e = range(10,10);
console.log(a); // [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
console.log(b); // [ 2, 3, 4, 5 ]
console.log(c); // [ 5, 4, 3, 2 ]
console.log(d); // [ -1, 0, 1, 2, 3 ]
console.log(e); // [ 10 ]
console.log(range('test', 10)); // TypeError
console.log(range(1, 'test')); // TypeError
console.log(sum(a)); // 55
console.log(sum(b)); // 14
console.log(sum(c)); // 14
console.log(sum(d)); // 5
console.log(sum(e)); // 10
console.log(sum('test')); // TypeError
here my answer, I'd glad if you give me feedback about this solution.
let arr = [];
function range(x, y) {
for (let i = x; i <= y; i++) {
arr.push(i);
}
return arr;
}
function sum(array) {
const many = array.reduce((total, number) => {
return total + number;
}, 0);
return many;
}
console.log(sum(range(1, 10)));
I want to check if an array contains an array of values rather then a value, for example
var arr = [1, 2, 3];
arr.contains([1, 2]); // returns true
// and
arr.contains([1, 5]); // must be false
There is a method called contains in underscore _.contains but it works like .indexof() in javascript instead it returns true and false and only find a value and not an array
Also there is a method Array.prototype.includes() in javascript that also take single input rather of an array
actually I want to check if second array has all the keys present in
first array I want to check with AND
for example [1,2,3,4].contains([1,4]) must return true where as [1,2,3,4].contains([1,9]) must return false, hope this makes sense,
I'm for now using underscores intersection and second array with
intersection result it must be same, its still long procedure....
You could use Array.prototype.includes() within a for loop
var arr = [1,2,3,4];
function contains(arr) {
for (var i = 0, included = true; i < arr.length
; included = included && this.includes(arr[i]), i++);
return included
}
Array.prototype.contains = contains;
console.log(arr.contains([1, 4]), arr.contains([9, 1]), arr.contains([1, 5]))
You can use a combination of every and indexOf
[1, 2].every(function(e) { return [1, 2, 3].indexOf(e) !== -1 });
would evaluate to true and
[1, 5].every(function(e) { return [1, 2, 3].indexOf(e) !== -1 });
would evaluate to false
The syntax would be a lot shorter in ES6 with arrow functions
[1, 2].every(e => [1, 2, 3].indexOf(e) !== -1)
Try this simple example
var arr = [1, 2, 3];
var searchArr = [1,2];
var isSearchArrAvailable = searchArr.filter( function(value){ if ( arr.indexOf( value ) == -1 ) { return true } else { return false; } } ).length == 0;
console.log( "is SearchArr Available " + isSearchArrAvailable );//this will print true
while var searchArr = [1,5] will print false.
You can refactor it into a function
function hasEveryElement(arr, searchArr)
{
return searchArr.filter( function(value){ if ( arr.indexOf( value ) == -1 ) { return true } else { return false; } } ).length == 0;
}
console.log( hasEveryElement([1,2,3], [1,2]) ) ;
console.log( hasEveryElement([1,2,3], [1,4]) ) ;
You can use every:
function contains(arr, arr2) {
return arr2.every(function (el) {
return arr.indexOf(el) > -1;
});
}
var arr = [1, 2, 3];
contains(arr, [1, 2]); // true
contains(arr, [1, 5]); // false
Alternatively, you could add a method to the prototype if you wanted the [].includes-style syntax, but call it something else - I've used hasAll.
if (!('hasAll' in Array.prototype)) {
Array.prototype.hasAll = function(arr) {
return arr.every(function(el) {
return this.indexOf(el) > -1;
}, this);
}
arr.hasAll([1, 2])); // true
arr.hasAll([1, 5])); // false
DEMO
Try this:
var arr = [1,2,3];
var temparr=[1,2];
var st=ArrOfArrSt(arr,temparr);
function ArrOfArrSt(arr,temparr){
var stArr= new Array();
if(temparr.length>0)
for(j in temparr){
if(arr.indexOf(temparr[j])==-1){
stArr.push('0');
}
}
return (stArr.length>0)?0:1; //whole array 0=not belong to / 1=belong to
}
I tried to remove some words according to the passing variable.
However, I wrote two versions of code which have minor different!
And they resulted in different kinds of output which I don't understand why!
So I need u guys help, and big big thanks for u guys!
This function will be accepting the different numbers of variables,
which might be ( [arr],1,2 ) or ( [arr],1,2,8,9...) etc,
and remove the the variable in the first array according to the passing numbers.
For example: destroyer ( [1, 2, 3, 4], 2, 3 ) --> output should be [1,4]
And here is my code. ( Notice the minor difference with bold fonts! )
function destroyer(arr) {
for ( var i = 1; i < arguments.length; i++ ){
arr = arguments[0].filter(function(value){
if( value == arguments[i]){
return false;
}else{
return true;
}
});
}
return arr;
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);
The output will be [1,2,3,1,2,3], which means value == arguments[i] doesn't work. However,
function destroyer(arr) {
for ( var i = 1; i < arguments.length; i++ ){
filter = arguments[i];
arr = arguments[0].filter(function(value){
if( value == filter){
return false;
}else{
return true;
}
});
}
return arr;
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);
This version works perfectly showing me [1,1].
So what's going wrong with the first version??? Thank you!!
The problem with the first one is that arguments applies to the .filter() callback function (the closest function scope, not the parent function scope) so arguments[i] is not what you want it to be.
You could copy those arguments into an actual array which you could then refer to from the .filter() callback.
function destroyer(arr) {
var args = [].slice.call(arguments, 1);
for ( var i = 0; i < args.length; i++ ){
arr = arr.filter(function(value){
if( value == args[i]){
return false;
}else{
return true;
}
});
}
return arr;
}
var a = destroyer([1, 2, 3, 1, 2, 3], 2, 3);
// show output
document.write(JSON.stringify(a));
Personally, I'd suggest a bit simpler version:
function destroyer(arr) {
var args = [].slice.call(arguments, 1);
return arr.filter(function(value) {
return args.indexOf(value) === -1;
});
}
var a = destroyer([1, 2, 3, 1, 2, 3], 2, 3);
// show output
document.write(JSON.stringify(a));
Which, can be written simpler in ES6 using the spread operator:
function destroyer(arr, ...args) {
return arr.filter(function(value) {
return args.indexOf(value) === -1;
});
}
var a = destroyer([1, 2, 3, 1, 2, 3], 2, 3);
// show output
document.write(JSON.stringify(a));
Or, if you prefer even shorter ES6 notation and want to use the new Array.prototype.includes():
function destroyer(arr, ...args) {
return arr.filter(value => !args.includes(value));
}
var a = destroyer([1, 2, 3, 1, 2, 3], 2, 3);
document.write(JSON.stringify(a));
I would write it like that in an es6 version:
function destroyer( arr, ...elem ) {
return arr.reduce( ( prev, next, index ) => elem.includes( arr[index] ) ? prev : prev.concat(next) , [])
}
I want to 'reduce' the array to only max values for each x (or index 0) value in a JavaScript multidimensional array.
My Array is as follows:
var mulitple = [["1/2013", 1],
["1/2013", 5],
["1/2013", 7],
["1/2013", 6],
["1/2013", 5],
["2/2013", 7],
["2/2013", 10],
["2/2013", 10],
["3/2013", 7],
["3/2013", 10],
["3/2013", 10],
["4/2013", 1],
["4/2013", 5],
["4/2013", 7],
["4/2013", 6],
["4/2013", 5],
["5/2013", 7]];
So the final result should be as follows:
[["1/2013", 7],
["2/2013", 10],
["3/2013", 10],
["4/2013", 7],
["5/2013", 7]];
How can I achieve this in JavaScript.
EDIT:
Aww man who voted my question down.
Anyhow, this is what I have come up with.
var max = 0;
var newarray = [];
for (var i = 1; i < mulitple.length; i++) {
if (mulitple[i - 1][0] == mulitple[i][0]) {
if (mulitple[i - 1][1] > max) {
max = mulitple[i - 1][1];
}
}
else {
if (mulitple[i - 1][1] > max) {
max = mulitple[i - 1][1];
}
newarray.push([mulitple[i - 1][0], max]);
max = 0;
}
}
newarray.push([mulitple[mulitple.length - 1][0], max]);
The problem that I am having is that I can't get that last value (for the lone record) to get in the array. This was my result after I ran the code above.
[["1/2013", 7], ["2/2013", 10], ["3/2013", 10], ["4/2013", 7], ["5/2013", 0]]
This assumes that original array is already sorted. If not, you will have to write additional function to sort out.
function findMaximums(data) {
var out = [], maximums = {}, order = new Set;
data.reduce(function(acc, pair) {
if (
// Accumulator has value and it's lower than current
(acc[pair[0]] && acc[pair[0]][1] < pair[1]) ||
// Accumulator doesn't have value
!acc[pair[0]]
) {
acc[pair[0]] = pair; // Store maximum in accumulator
order.add(pair[0]) // Store order in set
}
return acc;
}, maximums);
order.forEach(function(key) {
out.push(maximums[key]); // Populate out with maximums by order
});
return out;
}
findMaximums(multiple);
/*[
[
"1/2013",
7
],
[
"2/2013",
10
],
[
"3/2013",
10
],
[
"4/2013",
7
],
[
"5/2013",
7
]
]*/
Update 1: same, but without Set.
function findMaximums(data) {
var order = [];
var maximums = data.reduce(function(acc, pair) {
if (
// Accumulator has value and it's lower than current
(acc[pair[0]] && acc[pair[0]][2] < pair[1]) ||
// Accumulator doesn't have value
!acc[pair[0]]
) {
// Store maximum
acc[pair[0]] = pair;
// Store order
if (order.indexOf(pair[0]) === -1) {
order.push(pair[0])
}
}
return acc;
}, {});
return order.map(function(key) {
return maximums[key]; // Populate out with maximums by order
});
}
Update 2: Shorter version.
function findMaximums(data) {
return data.filter(function(p1, i1) {
return !data.some(function(p2, i2) {
return p1[0] === p2[0] && ( (p1[1] < p2[1]) || (p1[1] === p2[1] && i1 > i2) );
});
});
}
In this version I let pair to remain in output if there are no other pairs in input data that:
Have same month.
Have bigger value.
or
Have same value, but occur earlier than tested pair. This prevents duplicates.
Read here more about used Array methods: filter, some.
Assuming the array as defined in the original question, which is sorted to have each grouping together...
Completely untested code:
var reduced = [];
var groupName = '';
var groupMax;
var groupIndex;
var l = multiple.length; // Grab the array length only once
for (var i = 0; i < l; i++){
// Current Group Name doesn't match last Group Name
if (multiple[i][0] !== groupName) {
// Last Group Name is not empty (it's not the first Group)
if (groupName !== '') {
// Assume groupIndex has been set and grab the item
reduced.push(multiple[groupIndex]);
}
// Grab the new Group Name and set the initial Max and Index
groupName = multiple[i][0];
groupMax = multiple[i][1];
groupIndex = i;
}
// Current Value is bigger than last captured Group Max
if (multiple[i][1] > groupMax) {
// Grab the new Group Max and the current Index
groupMax = multiple[i][1];
groupIndex = i;
}
}
// Grab the last index, since there's no new group after the last item
reduced.push(multiple[groupIndex]);
There could be some syntax or logic errors. I didn't actually run this code, but I think the concept is correct.
Here's a tested version using a map to collect all the unique values, then the output is sorted by month/year and is independent of the order of the input data. This also works in all browsers (IE6+).
Working demo: http://jsfiddle.net/jfriend00/dk1tc73s/
function findLargest(list) {
var map = {}, output = [], item, key, val, current;
for (var i = 0; i < list.length; i++) {
item = list[i];
key = item[0];
val = item[1];
current = map[key];
if (current) {
// this date is in the map, so make sure to save the largest
// value for this date
if (val > current) {
map[key] = val;
}
} else {
// this date is not yet in the map, so add it
map[key] = val;
}
}
// map contains all the largest values, output it to an array
// the map is not in a guaranteed order
for (var key in map) {
output.push([key, map[key]])
}
// actually parse to numbers in sort so the comparison
// works properly on number strings of different lengths
function parseDate(str) {
var split = str.split("/");
return {m: +split[0], y: +split[1]};
}
// now sort the output
output.sort(function(t1, t2) {
var diffYear, a, b;
a = parseDate(t1[0]);
b = parseDate(t2[0]);
diffYear = a.y - b.y;
if (diffYear !== 0) {
return diffYear;
} else {
return a.m - b.m;
}
});
return output;
}