Related
I have an array of items :
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
// or more items
I have managed to split it into chunks by 3 items per array and pushed them into an array of arrays :
[
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
[10, 11] // <== the last one has less items than the others
]
I want to redistribute the items of the last array equally between the previous chunks :
// expected output
[
[1, 2, 3, 10],
[4, 5, 6, 11],
[7, 8, 9],
]
or even more complex like redistrbuting the items at random positions :
// expected output
[
[1, 2, 3, 10],
[4, 5, 6],
[7, 8, 9, 11],
]
so far this is what i have reached :
let array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
let arrayOfChunks = [];
let amount = 3;
for (let i = 0; i < array.length; i += amount) {
const chunk = array.slice(i, i + amount);
if (chunk.length === amount) {
arrayOfChunks.push(chunk);
} else {
console.log(chunk);
// output [10,11]
}
}
return arrayOfChunks;
I tried making another loop dpending on the arrayOfChunks.length = 3 where I could redistribute the items of the last array evenly into the arrayOfChunks, but sometimes the arrayOfChunks.length = 5 which require another splitting and merging into the previous generated equal chunks.
thanks for your help :)
After chunking the array normally, pop off the last subarray if the chunk number didn't divide the original array length evenly. Then, until that last popped subarray is empty, push items to the other subarrays, rotating indicies as you go.
const evenChunk = (arr, chunkSize) => {
const chunked = [];
for (let i = 0; i < arr.length; i += chunkSize) {
chunked.push(arr.slice(i, i + chunkSize));
}
if (arr.length % chunkSize !== 0) {
const last = chunked.pop();
let i = 0;
while (last.length) {
chunked[i].push(last.shift());
i = (i + 1) % chunked.length;
}
}
console.log(chunked);
};
evenChunk([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17], 7);
evenChunk([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 3);
So, I've the following arrays of odd numbers:
const oddNums = [1, 3, 5, 7, 9];
And I want to fill it with the missing even numbers to obtain the result below:
[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
I've done it like this and it works fine, but can it be done in a more concise manner, maybe using array methods?
const oddNums = [1, 3, 5, 7, 9];
const nums = [];
for (let i=0; i<oddNums.length; i++) {
nums.push(oddNums[i]);
nums.push(oddNums[i] + 1);
}
console.log(nums);
Note: The odd numbers would always be in sequence but might not begin with 1, for ex: [11, 13, 15] is a valid array. And the output for [11, 13, 15] should be [ 11, 12, 13, 14, 15, 16 ].
The only information you need is the first (odd) number and the size of the input:
const oddNums = [1, 3, 5, 7, 9];
const result = Array.from({length: oddNums.length*2}, (_, i) => i + oddNums[0]);
console.log(result);
Using Array.prototype.flatMap:
const
oddNums = [1, 3, 5, 7, 9],
nums = oddNums.flatMap(n => [n, n + 1]);
console.log(nums);
Using Array.prototype.reduce and Array.prototype.concat:
const
oddNums = [1, 3, 5, 7, 9],
nums = oddNums.reduce((r, n) => r.concat(n, n + 1), []);
console.log(nums);
Using Array.prototype.reduce and Array.prototype.push:
const
oddNums = [1, 3, 5, 7, 9],
nums = oddNums.reduce((r, n) => (r.push(n, n + 1), r), []);
console.log(nums);
We have an array of arrays like this:
const arrays = [
[0, 1, 2, 3, 4, 4, 4, 4],
[5, 6, 7, 8, 9, 10, 11, 11],
[2, 7, 10, 12],
[0, 7, 10, 14]
];
There may be duplicate elements in each array and that's fine.
But I'm after a proper solution to remove duplicate elements in each set comparing to lower sets!
So as we have a 0 in the first array and the last array, we should consider the 0 in last one a duplication and remove it...
the desired result would be:
[0, 1, 2, 3, 4, 4, 4, 4],
[5, 6, 7, 8, 9, 10, 11, 11],
[12],
[14]
It's a confusing issue for me please help...
You could collect the values in an object with index as value, and filter for values who are at the same index.
const
arrays = [[0, 1, 2, 3, 4, 4, 4, 4], [5, 6, 7, 8, 9, 10, 11, 11], [2, 7, 10, 12], [0, 7, 10, 14]],
seen = {},
result = arrays.map((array, i) => array.filter(v => (seen[v] ??= i) === i));
result.forEach(a => console.log(...a));
const arrays = [
[0, 1, 2, 3, 4, 4, 4, 4],
[4, 4, 5, 6, 7, 8, 9, 10, 11, 11],
[2, 7, 10, 12],
[0, 7, 10, 14]
]
let filtered = arrays.map((row, i) => {
// concat all previous arrays
let prev = [].concat(...arrays.slice(0, i))
// filter out duplicates from prev arrays
return row.filter(r => !prev.includes(r))
})
console.log(filtered)
We can do this using Array#reduce and maintain a seen Set, which will have all visited numbers from each array.
Once you iterate over an array you push all visited elements in the seen Set, then push a new array filtered by the elements not in the seen Set:
const arrays = [
[0, 1, 2, 3, 4, 4, 4, 4],
[5, 6, 7, 8, 9, 10, 11, 11],
[2, 7, 10, 12],
[0, 7, 10, 14]
];
const removeDupsInSibling = (arr) => {
let seen = new Set();
return arr.reduce((acc, a)=> {
const f = a.filter(v => !seen.has(v));
seen = new Set([...seen, ...a]);
acc.push(f);
return acc;
}, []);
}
console.log(removeDupsInSibling(arrays));
There are plenty of inefficient ways to do this, but if you want to do this in O(n), then we can make the observation that what we want to know is "which array a number is in". If we know that, we can run our algorithm in O(n):
for every element e in array at index i:
if index(e) == i:
this is fine
if index(e) < i:
remove this e
So let's just do literally that: we allocate an object to act as our lookup, and then we run through all elements:
const lookup = {};
const arrays = [
[0, 1, 2, 3, 4, 4, 4, 4],
[5, 6, 7, 8, 9, 10, 11, 11],
[2, 7, 10, 12],
[0, 7, 10, 14]
];
const reduced = arrays.map((array, index) => {
// run through the elements in reverse, so that we can
// safely remove bad elements without affecting the loop:
for(let i=array.length-1; i>=0; i--) {
let value = array[i];
let knownIndex = (lookup[value] ??= index);
if (knownIndex < index) {
// removing from "somewhere" in the array
// uses the splice function:
array.splice(i,1);
}
}
return array;
});
console.log(reduced);
For an alternative, where the loop+splice is taken care of using filter, see Nina's answer.
Simple, clean and high performance solution:
const arrays = [
[0, 1, 2, 3, 4, 4, 4, 4],
[5, 6, 7, 8, 9, 10, 11, 11],
[2, 7, 10, 12],
[0, 7, 10, 14]
];
const duplicates = {};
const answer = arrays.map( (array, level) => {
return array.filter( el => {
if ( duplicates[el] < level ) {
// return nothing; fine
} else {
duplicates[el] = level;
return el
}
})
});
console.log(JSON.stringify(answer))
here is on-liner and less-readable form:
const d = {}, arrays = [ [0, 1, 2, 3, 4, 4, 4, 4], [5, 6, 7, 8, 9, 10, 11, 11], [2, 7, 10, 12], [0, 7, 10, 14]];
const answer = arrays.map((a,l)=> a.filter(el=> d[el]<l ? 0 : (d[el]=l,el)));
console.log(JSON.stringify(answer))
const arrays = [
[0, 1, 2, 3, 4, 4, 4, 4],
[5, 6, 7, 8, 9, 10, 11, 11],
[2, 7, 10, 12],
[0, 7, 10, 14],
];
const output = arrays.reduce(
({ output, set }, current, i) => {
output[i] = current.filter((num) => !set.has(num));
[...new Set(output[i])].forEach((num) => set.add(num));
return { output, set };
},
{ output: [], set: new Set() }
).output;
console.log(output);
Gets the exact output you want:
[
[
0, 1, 2, 3,
4, 4, 4, 4
],
[
5, 6, 7, 8,
9, 10, 11, 11
],
[ 12 ],
[ 14 ]
]
I want to do bulk delete from an array using typescript.
I did it by using for loop.
this.versions = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
this.selectedVersions = [1, 2, 3, 4, 5, 6];
for (let i = 0; i < this.selectedVersions.length; i++) {
this.versions = this.removeObjectFromArray(this.versions, this.selectedVersions[i]);
}
//removing a object from array
removeObjectFromArray(listOfdata: any, data: any) {
let index = listOfdata.findIndex(function (o) {
return o === data;
})
listOfdata.splice(index, 1);
return listOfdata;
}
But I don't like to use for loop.so let me know how to do bulk delete in array using typescript.
You can just use array filter:
var versions = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
var selectedVersions = [1, 2, 3, 4, 5, 6];
versions = versions.filter(el => !selectedVersions.includes(el));
console.log(versions);
Use a filter
// will only keep elements that are not included in selectedVersions
this.versions = this.versions.filter(i => !this.selectedVersions.includes(i));
Make use of array.filter and filter the contents of the arrays based on the condition
let versions = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
let selectedVersions = [1, 2, 3, 4, 5, 6];
versions = versions.filter(arrayVal => selectedVersions.indexOf(arrayVal) == -1 );
// checks for the values of selected Versions agains each value of versions and returns accordindly
console.log(versions)
I think you can use indexOf, like this:
this.versions = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
this.selectedVersions = [1, 2, 3, 4, 5, 6];
for (let i = 0; i < this.selectedVersions.length; i++)
{
if(this.versions.indexOf(this.selectedVersions[i])!==-1){
this.versions.splice(index, 1);
}
}
I would personally use a filter for this to preserve the original arrays:
let nonSelectedVersions = this.versions.filter((e) => this.selectedVersions.indexOf(e) !== -1);
Use array.filter() instead of for loop
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
Typescript is the same as javascript, you can us all your knowledges from JS.
How do I remove a particular element from an array in JavaScript?
You can use for-of loop as well, if you TS compiler support
var versions = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
var selectedVersions = [1, 2, 3, 4, 5, 6];
for ( val of selectedVersions) {
if(selectedVersions.indexOf(val)!=-1){
versions.splice(versions.indexOf(val), 1);
}
}
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;
}