Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed last year.
Improve this question
Let's imagine I have to arrays:
const array1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const array2 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
array2.map((elem, index) => {
// Looking here to return the value from array1 by index
});
From array2 I am looking to return values from array1, by the index position, but just in the range 1 -10. The idea is that it should go in a kind of circle where the start value is 1 and end value is 10.
The expected output for the above example is:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5]
More examples of how it should work:
Index 1 from array2 -> return 1 from array1
Index 10 from array2 -> return 10 from array1
Index 12 from array2 -> return 2 from array1
Index 20 from array2 -> return 10 from array1
Index 999 from array2 -> return 9 from array1
Index 1225 from array2 -> return 5 from array1
You can use the remainder operator (%):
const array1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const array2 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
const result = array2.map((elem, index) => array1[index % array1.length]);
console.log(result);
You really don't need to have array2, which provides no information other than its length -- the zeroes are not relevant. So given just the length, it could be as follows:
const array1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const length = 15;
const result = Array.from({length}, (elem, index) => array1[index % array1.length]);
console.log(result);
You probably don't need two arrays to achieve this. You could use the modulo operator to perform 'wrap around' like lookups.
For example:
array1[index % (array1.length)]
if you absolutely need the info in a second array, something like this would achieve the desired result:
array2.forEach((_, index) => array2[index] = array1[index % (array1.length)]);
I have an array as below
var a = [ [ 5, 5, 1, -4 ], [ 3, 7, 3, -1 ], [ 7, 3, 4, 1 ], [ 5, 5, 5, 0 ] ]
Every nested array index:2 element indicates its distance from 0(Zero) and every index:3 element indicate how near it is to its next poll point.
I am trying to sort this array so I can get nested array which is near to index:0 with reference to index:2 element and its next poll point is very near.
For example, my answer here is [ 3, 7, 3, -1 ] because
though [ 5, 5, 1, -4 ] , index:2 is very near to 0 its next point is located at after/before 4 positions. But for [ 3, 7, 3, -1 ] next poll point is at one position.
I tried with sorting like below
js
inDiff = inDiff.sort( function(a,b ){
if ( a[2] < b[2]) {
if ( Math.abs(a[3]) < Math.abs(b(3)) ){
return Math.abs(b[3]) - Math.abs(a[3]);
}
}
});
Update 1
As asked in the comment I am adding explanation to each element of the nested array. For example in nested array [ 5, 5, 1, -4 ]
Index:0: Value 5 Represents 1st Number that I am looking for
Index:1 Value 5 Represents 2nd Number ( next poll point number)
By Adding these two numbers I will achieve my requirement of finding two numbers which can sum up for 10.
Index 2 : Value 1 : Indicates index of 1st number nothing but 5 in the source array
Index 3 : Value -4 : Indicates difference between indexes of Index:0 and Index:1 number of nested array from source array.
But nothing happens with my array.
Any help, much appreciated.
Assuming the input always follows requirements this demo uses .reduce()
const AA = [
[5, 5, 1, -4],
[3, 7, 3, -1],
[7, 3, 4, 1],
[5, 5, 5, 0]
];
let result = AA.reduce((min, now, idx, AoA) => {
let distTo0 = array => Math.abs(Math.floor(array[2]) + Math.floor(array[3]));
min = distTo0(now) < distTo0(min) ? now : min;
return min;
});
console.log(result);
The following demo includes all of the rules as I understood them:
const crazyLogic = arrayOfArrays => {
let AAClone = JSON.parse(JSON.stringify(arrayOfArrays));
const shared = AAClone[0][0] + AAClone[0][1];
const rules = [`Must be an array of number arrays`, `The sum of index 0 and 1 of each sub-array must be identical`, `Mismatched sub-array lengths`];
let message = !AAClone.every(sub => Array.isArray(sub)) ? rules[0] : !AAClone.every(sub => sub.every(num => num + 0 === num)) ? rules[0] : !AAClone.every(sub => sub[0] + sub[1] === shared) ? rules[1] : !AAClone.every(sub => sub.length === 4) ? rules[2] : null;
if (message !== null) {
return message;
}
return AAClone.reduce((min, now, idx, AoA) => {
let distTo0 = array => Math.abs(Math.floor(array[2]) + Math.floor(array[3]));
min = distTo0(now) < distTo0(min) ? now : min;
return min;
});
};
/* Input rules:
1. Must be an array of arrays (only numbers)
2. Each sum of subArray[0] + subArray[1] must be identical
3. Each subArray.length = 4
*/
// AAa returns [ 3, 7, 3, -1 ]
const AAa = [
[5, 5, 1, -4],
[3, 7, 3, -1],
[7, 3, 4, 1],
[5, 5, 5, 0]
];
// AA1 breaks rule 1
const AA1 = [
[5, 5, 1, -4],
[3, 7, 3, -1],
[7, 3, ' X', 1],
[5, 5, 5, 0]
];
// AAO breaks rule 1
const AAO = [
[5, 5, 1, -4],
[3, 7, 3, -1],
[7, 3, 4, 1],
[5, 5, 5, 0], {}
];
// AA2 breaks rule 2
const AA2 = [
[5, 5, 1, -4],
[3, 17, 3, -1],
[7, 3, 4, 1],
[5, 5, 5, 0]
];
// AA3 breaks rule 3
const AA3 = [
[5, 5, 1, -4],
[3, 7, 3, -1],
[7, 3, 4, 1],
[5, 5, 5]
];
console.log(crazyLogic(AAa));
console.log(crazyLogic(AA1));
console.log(crazyLogic(AAO));
console.log(crazyLogic(AA2));
console.log(crazyLogic(AA3));
I need an algorithm to rank elements of an array in Javascript.
Example : I have an array as follows:
[79, 5, 18, 5, 32, 1, 16, 1, 82, 13]
I need to rank the entries by value. So 82 should receive rank 1, 79 rank 2 etc.
If two entries have the same value they receive the same rank and the rank for a lower value is raised.
So for this array, the new ranking array would be:
[2, 7, 4, 7, 3, 9, 5, 9, 1, 6]
How can I do this?
var arr = [79, 5, 18, 5, 32, 1, 16, 1, 82, 13];
var sorted = arr.slice().sort(function(a,b){return b-a})
var ranks = arr.map(function(v){ return sorted.indexOf(v)+1 });
console.log(ranks);
Result :
[2, 7, 4, 7, 3, 9, 5, 9, 1, 6]
If you want to be compatible with old browsers, you may have to define a shim for indexOf and for map (note that if you want to do this very fast for very big arrays, you'd better use for loops and use an object as map instead of indexOf).
This won't work with older browsers because it uses ECMAScript 5 features, but it allows you to quickly and succinctly produce an array of rankings even for very large arrays. (It doesn't use indexOf which does a linear search and thus can be slow for large arrays.)
function cmp_rnum(a,b) {
// comparison function: reverse numeric order
return b-a;
}
function index_map(acc, item, index) {
// reduction function to produce a map of array items to their index
acc[item] = index;
return acc;
}
function ranks(v) {
var rankindex = v.slice().sort(cmp_rnum).reduceLeft(index_map, Object.create(null));
// reduceLeft() is used so the lowest rank wins if there are duplicates
// use reduce() if you want the highest rank
return v.map(function(item){ return rankindex[item]+1; });
}
Example output:
> ranks([79, 5, 18, 5, 32, 1, 16, 1, 82, 13]);
[2, 7, 4, 7, 3, 9, 5, 9, 1, 6]
function rank(arr, f) {
return arr
.map((x, i) => [x, i])
.sort((a, b) => f(a[0], b[0]))
.reduce((a, x, i, s) => (a[x[1]] =
i > 0 && f(s[i - 1][0], x[0]) === 0 ? a[s[i - 1][1]] : i + 1, a), []);
}
Usage:
rank([79, 5, 18, 5, 32, 1, 16, 1, 82, 13], (a, b) => b - a);
// [2, 7, 4, 7, 3, 9, 5, 9, 1, 6]
Looks a bit ugly, but it doesn't use indexOf() or an object/map, so not only does it run a little faster, but more importantly, it respects the meaning of "same rankedness" as defined by the comparison function. If one uses indexOf() or an object, "same rankedness" can only mean a === b or String(a) === String(b).
Alternatively, use findIndex():
function rank(arr, f) {
const sorted = arr.slice().sort(f)
return arr.map(x => sorted.findIndex(s => f(x, s) === 0) + 1)
}
JavaScript ES6 simple two lines solution.
var arrayRankTransform = arr => {
const sorted = [...arr].sort((a, b) => b - a);
return arr.map((x) => sorted.indexOf(x) + 1);
};
console.log(arrayRankTransform([79, 5, 18, 5, 32, 1, 16, 1, 82, 13]));
I am not good at Javascript but in PHP it can be done quite easily the following way. Somebody good at JavaScript can come up with the relevant code.
$marks = [79, 5, 18, 5, 32, 1, 16, 1, 82, 13];
public function getRank($marks) {
$rank = 1; $count = 0; $ranks = [];
//sort the marks in the descending order
arsort($marks,1);
foreach($marks as $mark) {
//check if this mark is already ranked
if(array_key_exists($mark, $ranks)) {
//increase the count to keep how many times each value is repeated
$count++;
//no need to give rank - as it is already given
} else {
$ranks[$mark] = $i+$j;
$i++;
}
return $ranks;
}
I needed the same 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]
IMHO several solutions here are incorrect as they do not correctly handle values that occur after repeated values. Such followers should get the next rank. The highest rank should equal the number of unique values in the array. This solution (in PHP) is, IMHO, correct. Basically #Suresh's solution with the bugs removed.
function rank($marks){
$rank = 1; $ranks = [];
rsort($marks,SORT_NUMERIC);
foreach($marks as $mark) {
if(!isset($ranks[$mark])) {
$ranks[$mark] = $rank++;
}
}
return $ranks;
}
This should work with duplicate keys in the array
function rank(arry) {
let sorted = arry.slice().sort(function (a, b) {
return b - a
});
let currentRank = sorted.length;
let rankValue = null;
let ranks = [];
sorted.forEach(value => {
if(value !== rankValue && rankValue !==null) {
currentRank--;
}
ranks.push({value,currentRank});
rankValue = value;
});
let mapRanksToArrayValues = arry.map(function (x) {
let _rank = null;
ranks.forEach( rank => {
if(rank.value === x ) {
_rank = rank.currentRank;
return;
}
});
return _rank;
});
return mapRanksToArrayValues;
}
I created Rank_JS Pro.
<script>https://cdn.statically.io/gl/maurygta2/mquery/master/Rank Tools/rank.js</script>
Basics Methods:
var a = {
b: 2,
c: 7
}
Rank_Tools.rank(a,(pos,name,value) => {
return pos + ". "+name+" "+value;
})
// result
// rank1 = 1. c 7
// rank 2 = 2. b 2
This alternate way doesn't require the input array to be sorted:
// O(n^2)
const rank = (arr) => {
// Create a temporary array to keep metadata
// regarding each entry of the original array
const tmpArr = arr.map(v => ({
value: v,
rank: 1,
}));
// Get rid of douplicate values
const unique = new Set(arr);
// Loops through the set
for (let a of unique) {
for (let b of tmpArr) {
// increment the order of an element if a larger element is pressent
if (b.value < a) {
b.rank += 1;
}
}
}
// Strip out the unnecessary metadata
return tmpArr.map(v => v.rank);
};
console.log(rank([2600, 200, 36, 36, 400, 2, 0, 0]));
// => [1, 3, 4, 4, 2, 5, 6, 6]
console.log(rank([79, 5, 18, 5, 32, 1, 16, 1, 82, 13]));
// => [2, 7, 4, 7, 3, 8, 5, 8, 1, 6]
I had the same homework and this one works well, also it's easier to understand if you are new to this.
function rankings(arr) {
let rankingsArr = [];
for (let i = 0; i < arr.length; i++) {
var rank = 1;
for (let j = 0; j < arr.length; j++) {
if (arr[j] > arr[i]) rank++;
}
rankingsArr.push(rank);
}
return rankingsArr;
}
Having an array of numbers setOfNumbers = [0, 3, 3, 2, 7, 1, -2, 9] I'd like to sort this set to have the smallest numbers on the end and beginning and the biggest in the center of the sorted set like this sortedSetNumbers = [0, 2, 3, 9, 7, 3, 1, -2].
const setOfNumbers = [0, 3, 3, 2, 7, 1, -2, 9];
const result = [0, 2, 3, 9, 7, 3, 1, -2];
function sortNormal(a, b) {
return true; // Please, change this line
}
const sortedSetNumbers = setOfNumbers.sort((a, b) => sortNormal(a, b));
if (sortedSetNumbers === result) {
console.info('Succeeded Normal Distributed');
} else {
console.warn('Failed Normal Distribution');
}
console.log(sortedSetNumbers);
I am sure it is possible to sort these numbers with the method Array.prototype.sort(), but how should this sorting function look like?
EDIT: The solution does not have to be solved with .sort(). That was only an idea.
This might be the most naive way to do it, but isn't it simply left, right, left, right... after sorting?
const input = [0, 3, 3, 2, 7, 1, -2, 9];
const expected = [0, 2, 3, 9, 7, 3, 1, -2];
const sorted = input.slice().sort();
const output = [];
let side = true;
while (sorted.length) {
output[side ? 'unshift' : 'push'](sorted.pop());
side = !side;
}
console.log(expected.join());
console.log(output.join());
Or simply:
const input = [0, 3, 3, 2, 7, 1, -2, 9];
const output = input.slice().sort().reduceRight((acc, val, i) => {
return i % 2 === 0 ? [...acc, val] : [val, ...acc];
}, []);
console.log(output.join());
A slightly different approach is to sort the array ascending.
Get another array of the indices and sort the odds into the first half asending and the even values to the end descending with a inverted butterfly shuffle.
Then map the sorted array by taking the value of the sorted indices.
[-2, 0, 1, 2, 3, 3, 7, 9] // sorted array
[ 1, 3, 5, 7, 6, 4, 2, 0] // sorted indices
[ 0, 2, 3, 9, 7, 3, 1, -2] // rebuild sorted array
var array = [0, 3, 3, 2, 7, 1, -2, 9].sort((a, b) => a - b);
array = Array
.from(array, (_, i) => i)
.sort((a, b) => b % 2 - a % 2 || (a % 2 ? a - b : b - a))
.map(i => array[i]);
console.log(array);
This solution is not really elegant, but it does it's job.
const setOfNumbers = [0, 3, 3, 2, 7, 1, -2, 9];
const alternation = alternate();
const sortedSetNumbers = sortNormal(setOfNumbers);
function sortNormal(start) {
const result = [];
const interim = start.sort((a, b) => {
return b - a;
});
interim.map(n => {
if (alternation.next().value) {
result.splice(0, 0, n);
} else {
result.splice(result.length, 0, n);
}
});
return result;
}
function* alternate() {
let i = true;
while (true) {
yield i;
i = !i;
}
}
console.log(sortedSetNumbers);