Make nested array to group array elements - javascript

I have array:
arr = [1,2,3,4,5,6,7,8,9,10,11,12,13,14];
Then I want to make group of 4 elements.
Every iteration, this array must be modified until it get's final face.
Step 1:
arr = [[1,2,3,4],5,6,7,8,9,10,11,12,13,14];
Step 2:
arr = [[1,2,3,4],[5,6,7,8],9,10,11,12,13,14];
Step 3:
arr = [[1,2,3,4],[5,6,7,8],[9,10,11,12],13,14];
Step 3:
arr = [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14]];
How is this possible?
I tried this:
var array = [1,2,3,4,5,6,7,8,9,10,11,12,13,14]
var i,j,temparray,chunk = 4;
for (i=0,j=array.length; i<j; i+=chunk) {
temparray = array.slice(i,i+chunk);
console.log(temparray);
}
But I don't know then how to save this chunk into own array and not in the new array.

Using Array#reduce method.
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14],
newArr = arr.reduce((acc, item, index) => {
if ((index) % 4 === 0) {
acc.push([item]);
} else {
acc[acc.length - 1].push(item);
}
return acc;
}, []);
console.log(newArr); // [ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9, 10, 11, 12 ], [ 13, 14 ] ]

You could splice the array until the length is smaller than the index of the last insertation.
var array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14],
i = 0;
while (i < array.length) {
array.splice(i, 0, array.splice(i, 4));
console.log(JSON.stringify(array));
i++;
}

lodash probably has better performances than my implementation, but if you are looking to do so with vanilla javascript then you can like this (though many other ways are possible):
var arr = [1,2,3,4,5,6,7,8,9,10,11,12,13,14];
var newArr = arr.reduce((acc, val, idx)=>{
if(idx % 4 === 0){
acc.push([]);
}
acc[acc.length-1].push(val)
return acc
}, [])
console.log(newArr);

The lodash method chunk will do this for you.
result = _.chunk(arr, 4);

function chunkArray(myArray, chunk_size){
var index = 0;
var arrayLength = myArray.length;
var tempArray = [];
for (index = 0; index < arrayLength; index += chunk_size) {
myChunk = myArray.slice(index, index+chunk_size);
// Do something if you want with the group
tempArray.push(myChunk);
}
return tempArray;
}
// Split in group of 3 items
var result = chunkArray([1,2,3,4,5,6,7,8], 3);
// Outputs : [ [1,2,3] , [4,5,6] ,[7,8] ]
console.log(result);

Just push it to the resulting array:
const chunk = 4, result = []
for (var i = 0, j = array.length; i < j; i += chunk) {
result.push(array.slice(i,i + chunk));
}

I thought it would be fun too if I add one more solution using recursive calls, Happy coding!
Test it here
function split(arr, offset, res){
//stop condition (offset exceeds len of array)
if(offset>arr.length)
return res;
//slice 4 elms
res.push(arr.slice(offset,offset+4));
//recursion
return split(arr, offset+4, res);
}
var res = split([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], 0, []);
console.log(res);

Related

Add every n items in an array

I have an array like so:
[5, 12, 43, 65, 34 ...]
Just a normal array of numbers.
What I wan't to do is write a function group(n, arr) which adds every n numbers in the array.
For example if I call group(2, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) it should return
[
3 //1+2,
7 //3+4,
11 //5+6,
15 //7+8,
19 //9+10,
11 //whatever remains
]
I haven't tried anything yet, I will soon update with what I can.
You can use .reduce as follows:
function group(n, arr) {
// initialize array to be returned
let res = [];
// validate n
if(n > 0 && n <= arr.length) {
// iterate over arr while updating acc
res = arr.reduce((acc, num, index) => {
// if the current index has no remainder with n, add a new number
if(index%n === 0) acc.push(num);
// else update the last added number to the array
else acc[acc.length-1] += num;
// return acc in each iteration
return acc;
}, []);
}
return res;
}
console.log( group(2, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) );
This approach features two loops, one for checking the outer index and anoter for iterating the wanted count of items for summing.
function group(n, array) {
const result = [];
let i = 0;
while (i < array.length) {
let sum = 0;
for (let j = 0; j < n && i + j < array.length; j++) {
sum += array[i + j];
}
result.push(sum);
i += n;
}
return result;
}
console.log(group(2, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]));
You could use Array.from to create the result array and then use its mapper to make the sums. These sums can be made by using reduce on the relevant slice of the array .
This is a functional programming solution:
const group = (step, arr) =>
Array.from({length: Math.ceil(arr.length/step)}, (_, i) =>
arr.slice(i*step, (i+1)*step).reduce((a, b) => a+b)
);
console.log(group(2, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]));

Sorting an array by chunks of 3

Assume that you have an array and want to divide it by chunks of 3. If the array is..
let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
...the new array would be
let newArr = [1, 4, 7, 10, 13, 2, 5, 8, 11, 3, 6, 9, 12]
// In other words:
1 2 3
4 5 6
7 8 9
10 11 12
13
The chunking part should be this code (if sorted like newArr):
let chunkedArr = _.chunk(_.toArray(newArr), 3);
...however I couldn't figure out how to sort the arr to newArr to be able to chunk in the right order. What is the proper way of handling such case?
Please note that the integers are just pseudo and I will use proper objects of array.
One option is to use ES6 reduce to group the array into a multidimensional array. Use concat to flatten the multidimensional array.
[].concat(...) - basically flattens the multidimensional array. Starting with an empty array [], you concat each secondary array. Use the spread operator (...) to reiterate each secondary array and concat each.
let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
let groups = 3;
let newArr = [].concat(...arr.reduce((c, v, i) => {
let k = i % groups;
c[k] = c[k] || [];
c[k].push(v);
return c;
}, []));
console.log(newArr);
Please try the following (jsfiddle):
//This version works for variable chunk sizes as well.
let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
alert(chunks(arr, 3)); //You can update chunk size here
function chunks(arr, chunkSize) {
var result = [];
var index = 0;
for (var i = 0; i < chunkSize; i++) {
for (var j = 0; j < arr.length / chunkSize; j++) {
if (arr[i + (chunkSize * j)] != null)
result[index++] = arr[i + (chunkSize * j)];
}
}
return result;
}
//This version only works for chunks of size 3.
let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
let subArr1 = [];
let subArr2 = [];
let subArr3 = [];
var result = [];
var i = 0, j = 0, k = 0;
for (var index = 0; index < arr.length; index++) {
if (index % 3 == 0) {
subArr1[i++] = arr[index];
}
if (index % 3 == 1) {
subArr2[j++] = arr[index];
}
if (index % 3 == 2) {
subArr3[k++] = arr[index];
}
}
result.push(subArr1, subArr2, subArr3);
alert(result);
Please check this it may help you. I also take a reference from here. Please check and let us know. any thing else you need.
Thanks
Here is the sample code.
var i,j,resArr,chunkSize = 10;
for (i=0,j=array.length; i<j; i+=chunk) {
resArr = array.slice(i,i+chunk);
}
const original = [1,2,3,4,5,6,7,8,9,10,11,12,13]
const chunks = 3
function getChunckedArr(arr, n) {
let sub = new Array(n);
let result = [];
for (let i=0; i<sub.length; i++)
sub[i] = []
arr.forEach(function (val, index){
let o = (index % n);
sub[o][sub[o].length] = val;
});
for (let i=0; i<sub.length; i++)
result.push.apply(result, sub[i]);
return result;
}
const chunked = getChunckedArr(original, chunks);

Alternative to array.splice in JavaScript

I am currently working on a project where I store numeric values in a JS array. After some changes it should be removed again. I currently use the array.splice method like this:
function removeA(arr, element) {
var index = arr.indexOf(element);
if (index >= 0) {
arr.splice(index, 1 );
}
return arr;
}
But this seems to give me issues on Safari. This piece of code works in every browser, like Chrome, Firefox, Opera. But not on Safari. It even works in the Technical Preview of Safari.
Does anyone have an alternative?
Thanks in advance :)
You have to slice before and after the index, and concat the results. Note that Array.prototype.slice() doesn't mutate the original array like Array.prototype.splice() does.
var arr = [0, 1, 2, 3, 4, 5, 6, 7];
var index = 5;
var result = arr.slice(0, index).concat(arr.slice(index + 1));
console.log(result);
Or using ES6 and array spread:
var arr = [0, 1, 2, 3, 4, 5, 6, 7];
var index = 5;
var result = [...arr.slice(0, index), ...arr.slice(index + 1)];
console.log(result);
You can use the built-in filter()
var array = [1,2,3,7,4,5,6,7,12,54,7,691];
var array = array.filter(x => x !== 7);
console.log(array);
Another Alternative to array.splice in JavaScript is array.reduce
var arr =[1,2,3,2,4,5,6,2];
var newarr = arr.reduce((acc, elem) => elem !== 2 ? acc.concat(elem) : acc, []);
console.log(newarr);
Try the slice() method
arr = arr.slice(index, 1 );
Sorry for late but hopefully it is useful for someone else
var arr = [32, 33, 16, 40, 55, 2, 41, 3, 10];
document.write("Array : "+arr);
document.write("<br>");
document.write("Removed Elements : "+mySplice(arr,2,2));
document.write("<br>");
document.write("Processed Array : "+arr);
function mySplice(array,index,count) {
var fixIndex = -1;
var ret = [];
arr = array.filter(function(element) {
fixIndex++;
if((fixIndex >= index && fixIndex < (index+count))) {
ret[ret.length]=element;
return false;
} else {
return true;
}
});
return ret;
}
Or you can use simple version (NOTE: it is simple but reversed)
var arr = [32, 33, 16, 40, 55, 2, 41, 3, 10];
document.write("Array : "+arr);
document.write("<br>");
document.write("Processed Array : "+mySplice_simple(arr,2,2));
function mySplice_simple(arr,index,count) {
fixIndex = -1;
return arr.filter(function(i) {
fixIndex++;
return !(fixIndex >= index && fixIndex < (index+count));
});
}
Or if you have to remove just one element then use this
var arr = [32, 33, 16, 40, 55, 2, 41, 3, 10];
document.write("Array : "+arr);
document.write("<br>");
document.write("Processed Array : "+mySplice_simple_v2(arr,2));
function mySplice_simple_v2(arr,index,count) {
fixIndex = -1;
return arr.filter(function(i) {
fixIndex++;
return fixIndex != index;
});
}
Some more ideas:
Option A flatMap():
Return an empty [] in order to "filter" elements. Less efficient but might be useful in case you want to add new elements as well.
const a = [3, 4, 5, 6];
const filter = 2;
const r = a.flatMap((v, j) => j !== filter ? v : []);
console.log(`Result: %o`, r); // Result: [3, 4, 6]
Example for filter + insert
const a = [3, 4, 5, 6];
const filter = 2;
const insert = 1;
const value = 4.5;
const r = a.flatMap((v, j) => {
if (j === filter) return [];
if (j === insert) return [v, value];
return v;
});
console.log(`Result: %o`, r); // Result: [3, 4, 4.5, 6]
Option B Array.from():
const a = [3, 4, 5, 6];
const filter = 2;
const r = Array.from({length: a.length -1}, (_, i) => a[i >= filter ? i + 1: i]);
console.log(`Result: %o`, r); // Result: [3, 4, 6]
Option C "Destructure":
const a = [3, 4, 5, 6];
const filter = 2;
const {[filter]: _, ...o} = a;
const r = Object.values(o);
console.log(`Result: %o`, r); // Result: [3, 4, 6]

Iterating over rows of 2-dimensional array containing arrays of different length

I have a function that picks all elements from a 2-dimensional array by its rows and returns a 1-dimensional array.
The array has a variable amount of columns and rows.
Example:
let arr = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]
];
Returns:
[1, 5, 9, 2, 6, 10, 3, 7, 11, 4, 8, 12]
The function i came up with:
convertList = (list) => {
let result = [];
let listTotalEntries = R.sum(R.map(R.length)(list));
let mod = R.modulo(R.__, list.length);
let counterRow = -1;
for (let i = 0; i < listTotalEntries; i++) {
if (mod(i) === 0) {
counterRow++;
}
if (list[mod(i)][counterRow]) {
result.push(list[mod(i)][counterRow]);
console.log(list[mod(i)][counterRow]);
}
}
console.log(result);
return result;
};
Question: This function works only with square matrices - how can i make it work with a variable length of the contained arrays?
Example:
let arr = [
[1, 2],
[],
[9, 10, 11, 12]
];
Should return:
[1, 9, 2, 10, 11, 12]
Thanks for your help!
Muff
You had a ramda.js tag in here. With Ramda, it's pretty simple, since there are two functions that will help:
const convertList = compose(flatten, transpose);
convertList(arr); //=> [1, 9, 2, 10, 11, 12]
transpose flips a matrix over its main diagonal, that is, changing rows to columns and vice versa. flatten turns a list of lists into a plain list. So composeing like this essentially creates the equivalent of list => flatten(transpose(list)).
You can see this in action on the Ramda REPL.
I suggest to go step-by-step through the arrays
var arr1 = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]],
arr2 = [[1, 2], [], [9, 10, 11, 12]];
function single(array) {
var r = [],
max = Math.max.apply(null, array.map(function (a) { return a.length; })),
i = 0, j,
l = array.length;
while (i < max) {
for (j = 0; j < l ; j++) {
i in array[j] && r.push(array[j][i]);
}
i++;
}
return r;
}
document.write('<pre>' + JSON.stringify(single(arr1), 0, 4) + '</pre>');
document.write('<pre>' + JSON.stringify(single(arr2), 0, 4) + '</pre>');
Did you try this simple one?
var singleDimensionArr = arr.reduce(function(prev,current){return prev.concat(current)});
For example
[
[1, 2],
[],
[9, 10, 11, 12]
].reduce(function(prev,current){return prev.concat(current)});
outputs [1, 2, 9, 10, 11, 12]
Edit:
Based on the inputs from OP below, since the concatenation needs to happen column wise
var max = Math.max.apply(null, arr.map(function (a) { return a.length; }));
var finalArr = []; for( var i = 0; i < max; i++)
{
for( var j = 0; j < arr.length; j++)
{
arr[j][i] ? finalArr.push(arr[j][i]) : "";
}
}
console.log(arr);
This example makes a big sparse array putting each item where it would belong if the array were square. Then it filters out null values which occur where no input item was present.
let arr = [
[1, 2],
[],
[9, 10, 11, 12]
];
var out = arr.reduce(function(o,n,i,a) {
for (var j=0;j<n.length;j++){
o[a.length * j + i] = n[j];
}
return o;
},[]).filter(function(n) {
return n !== null;
});
alert(JSON.stringify(out));

JavaScript Array Sorting/Ranking with Equal Ranks

After sorting an array of objects based on of their property values ("rating" in this case), how do you associate ranks for each object if there are ties between some of these values? Here's an example:
//Should be tied for 1st Rank
var obj1 = {
name: "Person1",
rating: 99
}
//Should be 3rd Rank
var obj2 = {
name: "Person2",
rating: 50
}
//Should be 2nd Rank
var obj3 = {
name: "Person3",
rating: 98
}
//Should be 4th Rank
var obj4 = {
name: "Person4",
rating: 0
}
//Should be tied for 1st Rank
var obj5 = {
name: "Person5",
rating: 99
}
Here's as far as I got:
var clients = [obj1, obj2, obj3, obj4, obj5];
var sorted = [];
for (var i = 0; i < clients.length; i++) {
sorted.push(clients[i]);
}
sorted.sort(function(a, b) {
return b.rating-a.rating;
});
Ultimately, I'd like to be able to get the rank using the object name, like this:
alert(sorted.indexOf(obj5) + 1);
Created a solution that worked, albeit ugly. Thanks jamie for some framework used in this:
for (var i = 0; i < clients.length; i++) {
sorted.push(clients[i]);
}
sorted.sort(function(a, b) {
return b.rating-a.rating;
});
for(var i = 0; i < sorted.length; i++) {
// original ranking
sorted[i].rank = i + 1;
}
function sortRanking() {
for (var k = 0; k < sorted.length; k++) {
for (var h = 1; h < sorted.length + 1; h++) {
if (sorted[k+h] !== undefined) {
if (sorted[k+h].tie !== true) {
if (sorted[k].rating === sorted[h + k].rating) {
sorted[k].rank = k + 1;
sorted[h + k].rank = k + 1;
sorted[k].tie = true;
sorted[h + k].tie = true;
}
}
}
}
}
}
sortRanking();
alert("Rank: " + obj3.rank);
Using ES6, here's how you can do it, adding a property rank to every client. Try the code snippet below.
function setRanks(clients) {
let currentCount = -1, currentRank = 0,
stack = 1; // consecutive clients with same rating
for (let i = 0; i < clients.length; i++) {
const result = clients[i];
if (currentCount !== result['rating']) {
currentRank += stack;
stack = 1;
} else {
stack++;
}
result['rank'] = currentRank;
currentCount = result['rating'];
}
}
// get the rank using the object name
function getRank(clientName) {
return clients.find(c => c.name === clientName)['rank'];
}
//Should be tied for 1st Rank
var obj1 = {
name: "Person1",
rating: 99
}
//Should be 3rd Rank
var obj2 = {
name: "Person2",
rating: 50
}
//Should be 2nd Rank
var obj3 = {
name: "Person3",
rating: 98
}
//Should be 4th Rank
var obj4 = {
name: "Person4",
rating: 0
}
//Should be tied for 1st Rank
var obj5 = {
name: "Person5",
rating: 99
}
var clients = [obj1, obj2, obj3, obj4, obj5];
clients.sort((c, other) => other.rating - c.rating);
setRanks(clients);
console.log(clients);
console.log(getRank('Person5'));
2nd attempt: although not quite there - i argue separating the ranking in to a different property rather than rely on the indexOf to find ranking is the way to go. You then have something clearer to manipulate when there is a tie. Still working it. Will be watching for best solution
for(var i = 0; i < sorted.length; i++) {
// original ranking
sorted[i].rank = i + 1;
}
function sortRanking() {
for(i=0; i< sorted.length; i++) {
var current = sorted[i];
var next = sorted[i + 1];
if(next === undefined || next.rating !== current.rating) {
console.log("we are done");
return "done";
}
if(next.rating === current.rating) {
for(var j = next + 1; j < sorted.length; j++) {
sorted[j].rank = sorted[j-1].rank;
}
next.rank = current.rank;
}
}
}
sortRanking();
console.log(sorted);
1st attempt - After playing around with for a bit. Here is a solution adding from your original logic:
var clients = [o1, o2, o3, o4];
var sorted = [];
for (var i = 0; i < clients.length; i++)
sorted.push(clients[i]);
sorted.sort(function (a, b) {
return clients.rating - clients.rating;
});
function checkForTieAndRating(x) {
// x parameter for object of interest
// need to get the one in front to determine if it is tied
// get index of obj of interest
var indexOfInterest = clients.indexOf(x);
var indexOfBefore = indexOfCurrent -1;
// if obj of interest is ranked #1 then return
if(indexOfBefore < 0) {
return indexOfInterest + 1;
} else {
// get the actual object before this one so you can check rating. put in variable so you can compare.
var objBefore = clients[indexOfBefore];
var ratingOfObjBefore = objBefore.rating;
if(ratingOfObjBefore === x.rating)
return "Tied for" + indexOfInterest;
}
}
// check ranking and if tie
checkForTieAndRating(obj2);
// other issue going this route - would be to then 1) alter the objects ranking following the objs that are tied - to
//Possible alternative solution: After working and about to submit it - I think it would be better to add a ranking property after the sort and manipulate the rankings from there if there are any tied.
If you want several records on the same place, you should probably use an additional immediate array, effectively grouping the elements.
I will use lodash for convinience, you should get the idea.
_.chain(clients).groupBy('rating').pairs().sortBy(0).reverse().pluck(1).value();
You loose your ability to use indexOf at this point, so you need to write your own getRank.
Again, with the help of lodash
// returns zero when no rank is found
var getRank = function(sortedArray, object) {
return 1 + _.findIndex(sortedArray, function(list) {
return _.contains(list, object);
});
};
Full working fiddle: http://jsfiddle.net/4WJN3/1/
I needed a similar piece of code for an operations scheduling script I was writing. I used objects and their properties/keys, which can have any value and can be accessed whenever needed. Also, as far as I read in some articles, the search of properties in objects can be faster than search in arrays.
The script below has three simple steps:
sort the values (ascending or descending doesn't matter for the rest of the script)
find the ranks and number of occurrences for each value
replace the given values with ranks using the data from step 2
Note! The below script will not output duplicate ranks, but instead increments ranks for duplicate values/elements.
function rankArrayElements( toBeRanked ) {
// STEP 1
var toBeRankedSorted = toBeRanked.slice().sort( function( a,b ) { return b-a; } ); // sort descending
//var toBeRankedSorted = toBeRanked.slice().sort( function( a,b ) { return a-b; } ); // sort ascending
var ranks = {}; // each value from the input array will become a key here and have a rank assigned
var ranksCount = {}; // each value from the input array will become a key here and will count number of same elements
// STEP 2
for (var i = 0; i < toBeRankedSorted.length; i++) { // here we populate ranks and ranksCount
var currentValue = toBeRankedSorted[ i ].toString();
if ( toBeRankedSorted[ i ] != toBeRankedSorted[ i-1 ] ) ranks[ currentValue ] = i; // if the current value is the same as the previous one, then do not overwrite the rank that was originally assigned (in this way each unique value will have the lowest rank)
if ( ranksCount[ currentValue ] == undefined ) ranksCount[ currentValue ] = 1; // if this is the first time we iterate this value, then set count to 1
else ranksCount[ currentValue ]++; // else increment by one
}
var ranked = [];
// STEP 3
for (var i = toBeRanked.length - 1; i >= 0; i--) { // we need to iterate backwards because ranksCount starts with maximum values and decreases
var currentValue = toBeRanked[i].toString();
ranksCount[ currentValue ]--;
if ( ranksCount[ currentValue ] < 0 ) { // a check just in case but in theory it should never fail
console.error( "Negative rank count has been found which means something went wrong :(" );
return false;
}
ranked[ i ] = ranks[ currentValue ]; // start with the lowest rank for that value...
ranked[ i ] += ranksCount[ currentValue ]; // ...and then add the remaining number of duplicate values
}
return ranked;}
I also needed to do something else for my script.
The above output has the following meaning:
index - the ID of the element in the input array
value - the rank of the element from the input array
And I needed to basically 'swap the index with the value', so that I have a list of element IDs, arranged in the order of their ranks:
function convertRanksToListOfElementIDs( ranked ) { // elements with lower ranks will be first in the list
var list = [];
for (var rank = 0; rank < ranked.length; rank++) { // for each rank...
var rankFound = false;
for (var elementID = 0; elementID < ranked.length; elementID++) { // ...iterate the array...
if ( ranked[ elementID ] == rank ) { // ...and find the rank
if ( rankFound ) console.error( "Duplicate ranks found, rank = " + rank + ", elementID = " + elementID );
list[ rank ] = elementID;
rankFound = true;
}
}
if ( !rankFound ) console.error( "No rank found in ranked, rank = " + rank );
}
return list;}
And some examples:
ToBeRanked:
[36, 33, 6, 26, 6, 9, 27, 26, 19, 9]
[12, 12, 19, 22, 13, 13, 7, 6, 13, 5]
[30, 23, 10, 26, 18, 17, 20, 23, 18, 10]
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7]
[7, 7, 7, 7, 7, 2, 2, 2, 2, 2]
[2, 2, 2, 2, 2, 7, 7, 7, 7, 7]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
rankArrayElements( ToBeRanked ):
[0, 1, 8, 3, 9, 6, 2, 4, 5, 7]
[5, 6, 1, 0, 2, 3, 7, 8, 4, 9]
[0, 2, 8, 1, 5, 7, 4, 3, 6, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[5, 6, 7, 8, 9, 0, 1, 2, 3, 4]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
convertRanksToListOfElementIDs( rankArrayElements( ToBeRanked ) ):
[0, 1, 6, 3, 7, 8, 5, 9, 2, 4]
[3, 2, 4, 5, 8, 0, 1, 6, 7, 9]
[0, 3, 1, 7, 6, 4, 8, 5, 2, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[5, 6, 7, 8, 9, 0, 1, 2, 3, 4]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
function rank(arr) {
var ret = [];
var s = [];
var i = 0;
var _key_;
for (_key_ in arr) {
var v;
v = arr[_key_];
if (!s[v]) {
s[v] = ++i;
}
ret.push( {
'Mark': v,
'Rank': s[v]
});
}
return ret;
}
var marks = [
65,
41,
38,
38,
37,
37,
92,
84,
84,
84,
83
];
marks.sort(function(a, b) {
return b-a;
});
var rank = rank(marks);
console.log(rank);
Concise, efficient, flexible.
Items with same score have same ranks, yet the next different score get a rank shifted by n (based on index). Input must be a array sorted by values of the sourceColumn. Two versions of the code, pick the one you like :
for(){ } loop
array.map()
var studentsSortedByGrades = [
{ name: "A", grade: 5 },
{ name: "B", grade: 3 },
{ name: "C", grade: 3 },
{ name: "D", grade: 2 },
];
var addRankFORLOOP = function(sortedArr,sourceColumn,newColumn){
for(var i = 0; i<sortedArr.length; i++){ //
sortedArr[i][newColumn] =
i===0 || sortedArr[i][sourceColumn] !== sortedArr[i-1][sourceColumn] ? i+1 // anytime new grade appears, rank=i
: sortedArr[i-1][newColumn] // elseIf: equal grade, then equal rank
}
return sortedArr;
};
/*//OR
var addRankMAP = function(sortedArr,sourceColumn,newColumn){
return sortedArr.map((item,i) => {
item[newColumn] = i===0 || sortedArr[i][sourceColumn] !== sortedArr[i-1][sourceColumn] ? i+1 // anytime new grade appears, rank=i
: sortedArr[i-1][newColumn] // elseIf: equal grade, then equal rank
return item; })
}; /**/
var withRanks = addRankFORLOOP(studentsSortedByGrades,'grade','rank');
console.log(withRanks) // ranks: 1,2,2,4

Categories

Resources