I'm trying to loop through an array in order to group and count totals.
For example:
var farm = [['Cats', 3], ['Cats', 4], ['Mice', 2], ['Dogs', 5]];
I would like to go through and first see 'Cats', then add all the cat values up, then repeat the process for other unique categories.
The final output would be:
Cats (7)
Mice (2)
Dogs (5)
Currently, I'm trying to accomplish it this way, but I'm obviously making a rookie mistake somewhere.
var farm = [];
farm.push(['Cats', 3], ['Cats', 4], ['Mice', 2], ['Dogs', 5]);
var animalCounter = function(array){
var list = '';
for(var i = 0; i<array.length; i++){
var animalID = array[i][0];
var animalCount = 0;
for (var x; x<array.length; x++){
if(animalID == array[x][0]){
animalCount += array[x][0] - 1;
}
list += animalID + " (" + animalCount + ")\n";
}
}
alert(list);
}
animalCounter(farm);
use an object to add similar animals together
var farm = [];
farm.push(['Cats', 3], ['Cats', 4], ['Mice', 2], ['Dogs', 5]);
var o = {};
for (var i=farm.length; i--;) {
var key = farm[i][0],
val = farm[i][1];
o[key] = key in o ? o[key] + val : val;
}
FIDDLE
Then it's easy to create the list
for (var key in o) {
list += key + " (" + o[key] + ")\n";
}
FIDDLE
You're missing an outer layer of brackets:
var farm = [['Cats', 3], ['Cats', 4], ['Mice', 2], ['Dogs', 5]];
What you had will end up being the same as
var farm = ['Dogs', 5];
The comma operator does strange things, especially in a var statement because , also separates individual variable declarations and initializations.
I'd probably do it a bit differently:
var farm = [];
farm.push(['Cats', 3], ['Cats', 4], ['Mice', 2], ['Dogs', 5]);
var animalCounter = function(array){
var animalObject = {};
for(var i = 0; i<array.length; i++){
var animalID = array[i][0];
var animalCount = array[i][1];
if(animalObject[animalID]) {
animalObject[animalID] += animalCount;
} else {
animalObject[animalID] = animalCount;
}
}
return animalObject;
}
The first function, animalCounter, creates an object that maps animal names to the numbers in the array. So for your example, it will return an object that looks like this:
{ 'Cats': 7, 'Mice': 2, 'Dogs': 5 }
From there, once you have the object, it's trivial to create a string to output this data in whatever format you like:
var counter = animalCounter(farm);
var list = '';
for(var key in counter) {
list += key + ': ' + counter[key] + ', ';
}
console.log(list); //=> Cats: 7, Mice: 2, Dogs: 5,
The reason your initial function didn't work is because it didn't take into account that there might be more than one instance of the same animal in your array. If your original array was [['Cats', 7], ['Mice', 2], ['Dogs', 5]] it would have been fine, but because your Cats entry was split into ['Cats', 4], ['Cats', 3], your original code saw that as two distinct animals. Notice in my function:
if(animalObject[animalID]) {
animalObject[animalID] += animalCount;
} else {
animalObject[animalID] = animalCount;
}
The code checks to see if the animal is already stored in the new object, and if it is, increments the count, rather than creating a new counter from 0. This way it deals with any duplicates.
I see three problems:
setting animalCounter equal to the number of animals - the new number of animals replaces whatever might already have been stored in animalCounter, so nothing is added up here
creating the animalCounter variable within the loop - if var animalCount is inside the loop, then you actually have a completely new variable for each element of the array
using a single variable for all the types of animals
Instead, try this:
var farm = [];
farm.push(['Cats', 3], ['Cats', 4], ['Mice', 2], ['Dogs', 5]);
var animalCounter = function (array) {
var list = '',
catsCount = 0,
miceCount = 0,
dogsCount = 0;
for (var i = 0; i < array.length; i++) {
var animalID = array[i][0];
var animalCount = array[i][1];
if (animalID === 'Cats') {
catsCount += animalCount;
} else if (animalID === 'Mice') {
miceCount += animalCount;
} else if (animalID === 'Dogs') {
dogsCount += animalCount;
}
}
list = 'Cats(' + catsCount + ') Mice(' + miceCount + ') Dogs(' + dogsCount + ')';
alert(list);
}
animalCounter(farm);
There are separate variables for each type of animal, and the value in the array is added onto the correct counter variable.
Or, for a more organized solution:
var farm = []; farm.push(['Cats', 3], ['Cats', 4], ['Mice', 2],
['Dogs', 5]);
var animalCounter = function (array) {
var list = '',
animalCounters = {};
for (var i = 0; i < array.length; i++) {
var animalID = array[i][0];
var animalCount = array[i][1];
animalCounters[animalID] = (animalCounters[animalID] || 0) + animalCount;
}
for (var id in animalCounters) {
list += id + " (" + animalCounters[id] + ")\n";
}
alert(list);
} animalCounter(farm);
In this code, the animalCounters variable is an object. JavaScript objects act like associative arrays, which lets us use the animal ID string as a "key" to an integer that is the animal sum. Each type of animal is a property of the animalCounters object, with the sum for that type of animal as its value.
I used some slightly obscure notation here, so I'll explain.
animalCounters[animalID]
This is just a different method of referring to properties of an object. In JavaScript, animalCounters.Cats and animalCounters["Cats"] access the same thing. But, if you don't know for sure that the type of animal will be Cats, you need "Cats" (or whatever other kind of animal) to be in a variable. The animalCounters["Cats"] notation takes a string, so you can say this and it will work:
var animalID = "Dogs";
alert(animalCounters[animalID);// returns animalCounters.Dogs
animalCounters[animalID] = (animalCounters[animalID] || 0) + animalCount;
Here, the (animalCounters[animalID] || 0) is saying that if animalCounters[animalID] already has a value, add that value to animalCount, otherwise add 0 to animalCount. This is necessary because if you try to add animalCounters[animalID] to animalCount before animalCounters[animalID] has been set to anything, the addition won't work right.
Just for funsies... (not very practical)
var farm = [['Cats', 3], ['Cats', 4], ['Mice', 2], ['Dogs', 5]];
farm.reduce(function(a, b, i){
var r = (i==1 ? a.slice() : a),
j = r.indexOf(b[0]);
if(j >= 0){
r[j+1] += b[1];
return r;
} else return r.concat(b);
}).reduce(function(a, b, i){
return i%2 ? a+' ('+b+')' : a+'\n'+b;
});
Rough explanation:
Iterate over each element of farm reducing the 2D array to a flat array where every odd index is the "count" that corresponds to the previous element - taking note to check if the "key" in the even index already exists in the array (in which case update it's count respectively). The slice call is in there just to make sure that the original array is left unmodified. This results in an array looking like:
["Cats", 7, "Mice", 2, "Dogs", 5]
This new array is reduced once more, this time concatenating each element into a single string - formatting dependent on whether or not the current iteration has an odd or even index.
Array.reduce is one of those functions that isn't supported in older browsers (if that is important) but there's a polyfill available on the MDN site.
When you access the amount of animals of a certain kind you made a simple mistake:
animalCount += array[x][0] - 1;
farm[x][0] will always return the animal's name which is a string, so when trying to subtract 1 from it it will result in NaN (Not a Number).
Also the first for loop: for(var i; i<array.length; i++){ ... cycles through all the array slots even if they were already counted, so cats would be counted twice so instead of cats counted as 7 they would amount to 14.
You need to create a copy of array and take off the slots already counted. The tricky part is copying the array by value and so that any changes to Temp won't change farm (see Copying Arrays):
var farm = [];
farm.push(['Cats', 3], ['Cats', 4], ['Mice', 2], ['Dogs', 5]);
function countAnimals(array) {
var Temp = [];
var d = 0;
//This while loop copies the array
while (d < farm.length) {
var s = array[d].toString();
Temp.push(s.split(","));
d++;
}
var list = "";
var done = 0;
while (done < array.length) {
if(Temp[0][1] == "Done") {
Temp.shift();
} else {
var animalID = Temp[0][0];
var count = parseFloat(Temp[0][1]);
Temp.shift();
var i = 0;
while (i < Temp.length) {
if(Temp[i][0] == animalID) {
count = count + parseFloat(Temp[i][1]);
Temp[i][1] = "Done";
}
i++;
}
list = list + "\n" + animalID + "("+count+")";
}
done++;
}
alert(list);
}
countAnimals(farm);
Related
Write a program to find count of the most frequent item of an array. Assume that input is array of integers.
Example:
Input array: [3, -1, -1, -1, 2, 3, -1, 3, -1, 2, 4, 9, 3]
Ouptut: 5
Most frequent number in example array is -1. It occurs 5 times in input array.
Here is my code:
function mostFrequentItemCount(collection) {
var copy = collection.slice(0);
for (var i = 0; i < collection.length; i++) {
var output = 0;
for (var x = 0; x < copy.length; x++) {
if (collection[i] == copy[x]) {
output++;
}
}
}
return output;
}
It seems to be just counting the reoccurrence of the first number in the array not the one that occurs the most. I can't figure out how to make it count the most occurring one.
If i didn't miss anything, and if you really want to find the count of the most frequent item of an array, i guess one approach would be this one:
function existsInCollection(item, collection) {
for(var i = 0; i < collection.length; i++) {
if(collection[i] === item) {
return true;
}
}
return false;
}
function mostFrequentItemCount(collection) {
var most_frequent_count = 0;
var item_count = 0;
var already_checked = [];
for(var i = 0; i < collection.length; i++) {
// if the item was already checked, passes to the next
if(existsInCollection(collection[i], already_checked)) {
continue;
} else {
// if it doesn't, adds to the already_checked list
already_checked.push(collection[i]);
}
for(var j = 0; j < collection.length; j++)
if(collection[j] === collection[i])
item_count++;
if(item_count > most_frequent_count)
most_frequent_count = item_count;
item_count = 0;
}
return most_frequent_count;
}
var items = [3, -1, -1, -1, 2, 3, -1, 3, -1, 2, 4, 9, 3];
alert(mostFrequentItemCount(items));
What happens here is:
On each item ('i' loop), it will run another loop ('j') through all items, and count how many are equal to the [i] item. After this second loop, it will be verified if that item count is greater than the most_frequent_count that we already have, and if it is, updates it.
Since we always use the same variable 'item_count' to check each number count, after the verification of each number we reset it to 0.
This may not be the best answer, but it was what occurred me at the moment.
EDIT:
I added a function to check if an item already exists in a list, to avoid the loop from check the same item again.
The problem is that you override the output variable each loop iteration, so after the for loop ends your output variable holds occurrences of the last element of input array.
You should use variables like var best_element = collection[0] and var best_element_count = -1 (initialized like this). After each inner loop you check if algo found any better solution (best_element_count < output) and update best_element.
Edit: following #Alnitak comment you should also reset the output variable after each inner loop iteration.
First you will need to construct a collection (or object) that contains the element and the count of occurances. Second you will need to iterate the result to find the key that has the highest value.
JSFiddle
function mostFrequentItemCount(collection) {
var output = {};
for (var i = 0; i < collection.length; i++) {
var item = collection[i];
if (!(item in output))
output[item] = 0;
output[item]++;
}
var result = [0, 5e-324];
for (var item in output) {
if (output[item] > result[1]) {
result[0] = parseFloat(item);
result[1] = output[item];
}
}
return result;
}
var input = [3, -1, -1, -1, 2, 3, -1, 3, -1, 2, 4, 9, 3];
var result = mostFrequentItemCount(input);
console.log(result);
The snippet above simply creates a new object (output) which contains a property for each of the unique elements in the array. The result is something like.
2:2
3:4
4:1
9:1
-1:5
So now we have an object with the property for the number and the value for the occurances. Next we then interate through each of the properties in the output for(var item in output) and determine which value is the greatest.
Now this returns an array with the value at index 0 being the number and the value at index 1 being the count of the element.
Check this solution.
var store = [3, -1, -1, -1, 2, 3, -1, 3, -1, 2, 4, 9, 3];
var frequency = {}; // array of frequency.
var max = 0; // holds the max frequency.
var result; // holds the max frequency element.
for(var v in store) {
frequency[store[v]]=(frequency[store[v]] || 0)+1; // increment frequency.
if(frequency[store[v]] > max) { // is this frequency > max so far ?
max = frequency[store[v]]; // update max.
result = store[v]; // update result.
}
}
alert(max);
So this update to your method will return an object with each key and the count for that key in the array. How you format an output to say what key has what count is up to you.
Edit: Updated to include the complete solution to the problem.
function mostFrequentItemCount(collection) {
var copy = collection.slice(0);
var results = {};
for (var i = 0; i < collection.length; i++) {
var count = 0;
for (var x = 0; x < copy.length; x++) {
if (collection[i] == copy[x]) {
count++;
}
}
results[collection[i]] = count;
}
return results;
}
var inputArray = [3, -1, -1, -1, 2, 3, -1, 3, -1, 2, 4, 9, 3];
var occurances = mostFrequentItemCount(inputArray);
var keyWithHighestOccurance = Object.keys(occurances).reduce(function(a, b){ return occurances[a] > occurances[b] ? a : b });
var highestOccurance = occurances[keyWithHighestOccurance];
console.log("Most frequent number in example array is " + keyWithHighestOccurance + ". It occurs " + highestOccurance + " times in the input array.");
Say I have the array [1,2,3,5,2,1,4]. How do I get make JS return [3,4,5]?
I've looked at other questions here but they're all about delete the copies of a number which appears more than once, not both the original and the copies.
Thanks!
Use Array#filter method twice.
var data = [1, 2, 3, 5, 2, 1, 4];
// iterate over elements and filter
var res = data.filter(function(v) {
// get the count of the current element in array
// and filter based on the count
return data.filter(function(v1) {
// compare with current element
return v1 == v;
// check length
}).length == 1;
});
console.log(res);
Or another way using Array#indexOf and Array#lastIndexOf methods.
var data = [1, 2, 3, 5, 2, 1, 4];
// iterate over the array element and filter out
var res = data.filter(function(v) {
// filter out only elements where both last
// index and first index are the same.
return data.indexOf(v) == data.lastIndexOf(v);
});
console.log(res);
You can also use .slice().sort()
var x = [1,2,3,5,2,1,4];
var y = x.slice().sort(); // the value of Y is sorted value X
var newArr = []; // define new Array
for(var i = 0; i<y.length; i++){ // Loop through array y
if(y[i] != y[i+1]){ //check if value is single
newArr.push(y[i]); // then push it to new Array
}else{
i++; // else skip to next value which is same as y[i]
}
}
console.log(newArr);
If you check newArr it has value of:
[3, 4, 5]
var arr = [1,2,3,5,2,1,4]
var sorted_arr = arr.slice().sort(); // You can define the comparing function here.
var nonduplicates = [];
var duplicates=[];
for (var i = 0; i < arr.length; i++) {
if (sorted_arr[i + 1] == sorted_arr[i]) {
duplicates.push(sorted_arr[i]);
}else{
if(!duplicates.includes(sorted_arr[i])){
nonduplicates.push(sorted_arr[i]);
}
}
}
alert("Non duplicate elements >>"+ nonduplicates);
alert("Duplicate elements >>"+duplicates);
I think there could exists option with Map.
function unique(array) {
// code goes here
const myMap = new Map();
for (const el of array) {
// save elements of array that came only once in the same order
!myMap.has(el) ? myMap.set(el, 1) : myMap.delete(el);
}
return [...myMap.keys()];
}
const array = [1,2,3,5,2,1,4];
//[11, 23, 321, 300, 50, 23, 100,89,300];
console.log(unique(array));
I have 10 different arrays. Each array has different numbers.
array1 = [1,2,3,4,5]
array2 = [6,7,8,9,10]
...
array 10 = [51,52,53,54]
let's say I pass in 7. Then I want to know which array it is from and want to return array number. So in this case it is going to be 2.
Should I write a switch statement for each array? Appreciate it in javascript.
try:
var arrays = [array1, array2, ..., array10];
for(var i=0; i<arrays.length; ++i) {
if (arrays[i].indexOf(value) != -1) {
console.log('found in array' + (i+1));
}
}
You cannot directly retrieve the name of array.The reason is this variable is only storing a reference to the object.
Instead you can have a key inside the same array which represent its name. Then indexOf can be used to find the array which contain the number , & if it is so, then get the array name
var array1 = [1,2,3,4,5];
array1.name ="array1";
var array2 = [6,7,8,9,10];
array2.name ="array2";
var array10 = [51,52,53,54]
array10.name ="array10";
var parArray = [array1,array2,array10]
function _getArrayName(number){
for(var o=0;o<parArray.length;o++){
var _tem = parArray[o];
if(parArray[o].indexOf(number) !==-1){
console.log(parArray[o].name);
}
}
}
_getArrayName(6) //prints array2
jsfiddle
One fast method should be using hash tables or as i would like to call LUT. Accordingly this job boils down to a single liner as follows;
var arrs = {
arr1 : [1,2,3,4,5],
arr2 : [6,7,8,9,10],
arr3 : [12,14,16,17],
arr4 : [21,23,24,25,27,20],
arr5 : [31,34,35,39],
arr6 : [45,46,44],
arr7 : [58,59],
arr8 : [66,67,69,61],
arr9 : [72,73,75,79,71],
arr0 : [81,85,98,99,90,80]
},
lut = Object.keys(arrs).reduce((p,c) => {arrs[c].forEach(n => p[n]=c); return p},{}),
findar = n => lut[n];
document.write("<pre>" + findar(12) + "</pre>");
One way to do this is have the arrays in an object and iterate over the keys/values. This method doesn't presume the arrays (and therefore their names) are in sequential order.
Note: this will always return a the first match from the function and terminate the search.
var obj = {
array1: [1, 2, 3, 4, 5],
array2: [6, 7, 8, 9, 10],
array3: [51, 52, 53, 54],
array4: [51, 52, 53, 54, 7]
}
function finder(obj, test) {
var keys = Object.keys(obj);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (obj[key].indexOf(test) > -1) {
return key.match(/\d+/)[0];
}
}
return false;
}
finder(obj, 7); // '2'
DEMO
If you want to find all instances of a value in all arrays the function needs to be altered slightly.
function finder(obj, test) {
var keys = Object.keys(obj);
var out = [];
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (obj[key].indexOf(test) > -1) {
out.push(key.match(/\d+/)[0]);
}
}
return out;
}
finder(obj, 7); // ['2', '4']
DEMO
So here is what I'm trying to do.
I have an array that contains more arrays of dates (which there are multiples of) and individual scores. It looks like this:
var example = [
["11/7/2015", 4],
["11/7/2015", 7],
["11/7/2015", 2],
["11/8/2015", 2],
["11/8/2015", 7],
["11/9/2015", 0],
["11/10/2015", 1]
];
My goal is to iterate through this entire array (it has around 900 cells), that can add/combine the scores of the dates that are similar, and overall removes all duplicate dates with the scores added together.
So the end result of the first array should look like this:
var example = [
["11/7/2015", 13],
["11/8/2015", 9],
["11/9/2015", 0],
["11/10/2015", 1]
];
As you can see, the duplicate dates were removed and the scores of each duplicate cell were added under one cell.
I tried doing this by using a for loop like this (using a duplicate of the array so I can use it as comparison to the original):
for(var i = 1; i < example.length; i--){
if(example[i][0] === dummyArray[i-1][0]){
example[i-1][1] += dummyArray[i][1];
example.splice(i,1);
} else{
}
}
But I can't use i-1 syntax inside the loop and not sure where to go from here. My goal is to do this in pure javascript and not use any libraries.
Here's one way of doing that:
var dateScoreAggregateMap = {};
example.forEach(function(pair){
if(dateScoreAggregateMap[pair[0]]){
dateScoreAggregateMap[pair[0]] += pair[1];
} else {
dateScoreAggregateMap[pair[0]] = pair[1];
}
});
example = Object.keys(dateScoreAggregateMap).map(function(date){
return [date, dateScoreAggregateMap[key]];
});
If you don't mind the result in object form:
var example = [
["11/7/2015", 4],
["11/7/2015", 7],
["11/7/2015", 2],
["11/8/2015", 2],
["11/8/2015", 7],
["11/9/2015", 0],
["11/10/2015", 1]
];
var out = example.reduce(function (p, c) {
if (!p[c[0]]) p[c[0]] = 0; // key doesn't exist in object, add it and set it to zero
p[c[0]] += c[1]; // add the score to the existing key
return p;
}, {});
alert(JSON.stringify(out));
First generate results through an object:
var obj = {};
example.map(function(el) {
el[0] in obj ? obj[el[0]] += el[1] : obj[el[0]] = el[1];
return obj;
});
el is the array pair. You ask if the date el[0] is in the object. If so, add the corresponding value. If not, set the key with the corresponding value.
Translate the object to an array:
var arr = Object.keys(obj).map(function(el) {
return [el, obj[el]];
});
or (what is equal):
var arr = [];
for(k in obj) {
var pair = [k, obj[k]];
arr.push(pair);
}
JSFiddle
I have this array, but not in any guaranteed order:
[ [2,1], [2,2], [2,3], [3,1], [3,2], [4,1], [4,2], [4,3], [4,4], [5,1], [5,2] ]
I need to cycle through it, match the ones with the same arr[0] value, and then remove the one with the highest value at arr[1]. It should end up looking like this:
[ [2,1], [2,2], [3,1], [4,1], [4,2], [4,3], [5,1] ]
I'm not sure exactly how to iterate through this accurately. Most places I have seen ways to filter complex objects, or remove single values from one-dimensional arrays.
I have only really gotten into using arrays in the last few days. Thanks for the help!
Okay I have two solutions. version1 is basic and could use some optimizing while version2 should be faster with bigger, evenly distributed lists.
If you're only going to have a few items, use one. It only has a few lines of code, so it won't be a distraction. If you have a big array and it'll be pretty evenly distributed, then use two.
I actually did a test with the sample data, and version2 has less iterations than version1. V1 ran 11 times in the outer loop, and 79 times in the inner loop. V2 ran 11 times in the first outer loop, 4 times in the second one. The inner loop of the second loop ran 11 times, and the loop inside that ran only 7 times. So the total iterations of v2 was about 40% of v1. When I double the items, v2 only uses 30% of the iterations.
Version2 has a couple of other potential advantages.
I believe Array.push has a higher performance cost than Array[index] =. If that's true, then you know that newAry will have a final length of the origianl array's length - the length of the indicies array length. So you can initialize newAry with that length, keep a counter variable, and then do something like newAry[counter++] = someVal.
There was some discussion if you wanted to keep a result if there was only one. If that is the case, it is easy to do a check at the start of the second loop: if (iVal.length == 1) // add to newAry else do j,k loops.
Version 1
function version1(ary) {
var newAry = [];
var iVal, jVal;
for (var i = 0, il = ary.length; i < il; i++) {
iVal = ary[i];
for (var j = ary.length - 1; j >= 0; j--) {
if (i != j) {
jVal = ary[j];
if (iVal[0] == jVal[0] && iVal[1] < jVal[1]) {
newAry.push(iVal);
break;
}
}
}
}
return newAry;
}
Version 2
function version2(ary) {
var indices = [];
var values = [];
var newAry = [];
var iVal,
index,
highestFound,
lowFound;
for (var i = 0, il = ary.length; i < il; i++) {
var iVal = ary[i];
if ((index = indices.indexOf(iVal[0])) == -1) {
indices.push(iVal[0])
values.push([ iVal[1] ]);
index++;
}
else {
values[index].push(iVal[1])
};
}
for (var i = 0, il = values.length; i < il; i++) {
iVal = values[i];
highestFound = false;
for (var j = 0, jl = iVal.length; j < jl; j++) {
if (!highestFound) {
lowFound = false;
for (var k = j + 1, kl = iVal.length; k < kl; k++) {
if (iVal[j] < iVal[k]) {
lowFound = true;
newAry.push([indices[i], iVal[j]]);
k = kl;
}
}
if (!lowFound) {
highestFound = true;
}
}
else {
newAry.push([indices[i], iVal[j]]);
}
}
}
return newAry;
}
jsFiddle
jsFiddle with Counters
Based on what you've given so far, here's the code I came up with:
var foo = [
[2, 1],
[2, 2],
[2, 3],
[3, 1],
[3, 2],
[4, 1],
[4, 2],
[4, 3],
[4, 4],
[5, 1],
[5, 2]
],
temp = [];
foo.forEach(function (value, index) {
if (typeof temp[value[0]] === 'undefined') {
temp[value[0]] = {
highestValue: value[1],
position: index
};
} else {
if (temp[value[0]].highestValue < value[1]) {
temp[value[0]].highestValue = value[1];
temp[value[0]].position = index;
}
}
});
temp.forEach(function(value, index) {
delete foo[value.position];
});
Do note that if you have in foo for instance [6,1], it will be deleted as well.
Making use of the excellent lodash.js library:
var ary = [ [2,1], [2,2], [2,3], [3,1], [3,2], [4,1], [4,2], [4,3], [4,4], [5,1], [5,2] ];
var results = _(ary)
// Group by the first value
.groupBy(function(pair) {
return pair[0];
})
// Turn the groups into arrays
.map(function(group) {
// Sort by the second value
var sorted = _.sortBy(group, function(pair) {
return pair[1];
});
// Keep all but the highest value
return _.take(sorted, sorted.length-1);
})
// Remove the groupings
.flatten(true)
// Unwrap the results
.value();
console.log(results);
jsFiddle: http://jsfiddle.net/dmillz/BhSDT/
I hope that helps!