Related
I'm still a junior at web dev and I am trying to solve this problem.
I have to find the number of matching pairs in these arrays:
var ar1 = [10, 20, 20, 10, 10, 30, 50, 10, 20] // return 3 (2 pairs of 10 and 1 pair of 20)
var ar2 = [1, 1, 3, 1, 2, 1, 3, 3, 3, 3] // return 4 (2 pairs of 1 and 2 pairs of 3)
// I started to write my logic below but I'm stuck, could you please help me to solve this problem ?
// The last result I am returning is a filtered array with all the nbs that are superior to 1 and then can't figure out how to get the result of matching pairs :-(
function countPairs(n, ar) {
const count = {};
ar.forEach((nb) => (count[nb] = (count[nb] || 0) + 1));
const values = Object.values(count);
const filter = values.filter((value) => value > 1);
return filter;
}
// 9 and 10 are the length of the arrays
console.log(countPairs(9, ar1))
console.log(countPairs(10, ar2))
Thank you very much for your help!
Perhaps there is a faster/better way to calculate this than this O(2n) solution, but it's something:
var ar1 = [10, 20, 20, 10, 10, 30, 50, 10, 20] // return 3 (2 pairs of 10 and 1 pair of 20)
var ar2 = [1, 1, 3, 1, 2, 1, 3, 3, 3, 3] // return 4 (2 pairs of 1 and 2 pairs of 3)
function countPairs(ar) {
var obj = {};
ar.forEach(item => {
obj[item] = obj[item] ? obj[item] + 1 : 1;
});
return Object.values(obj).reduce((acc, curr) => {
acc += Math.floor(curr / 2)
return acc;
}, 0);
}
console.log(countPairs(ar1))
console.log(countPairs(ar2))
This first calculates the number of occurences for each number and stores them in an Object. Once that is done, we reduce over the values and return the quotient from the division with 2 (to get the number of pairs in total).
Note: I removed the first argument from your function, because the array length is not needed as an argument. It can be obtained from the array you pass directly.
We can achieve this in O(n) time. Maintain an object which keeps track whether a number have been found before, if it was found before, then it makes up a pair, so we increment the pairs count. If not we make the entry of that number in the object 1
function countPairs(arr) {
let pairs = 0;
const obj = {};
arr.forEach(i => {
if (obj[i]) {
pairs += 1;
obj[i] = 0;
} else {
obj[i] = 1;
}
});
return pairs;
}
Simplest solution I can find:
create empty dictionary var t = {}; and use it to count each item in array arr.forEach (i => t[i] = (t[i] || 0) + 1);. After that take all keys Object.values(t) and sum .reduce((acc, p) => acc + ..., 0) each item counts divided by 2 p/2 with Int semantics of course Math.floor(...).
function countPairs(arr) {
var t = {};
arr.forEach (i => t[i] = (t[i] || 0) + 1);
return Object.values(t).reduce((acc, p) => acc + Math.floor(p/2), 0);
}
console.dir(countPairs([1,2,2,2,2,3]));
console.dir(countPairs([1,2,2,2,2,2,3]));
console.dir(countPairs([1,2,2,2,2,2,2,3]));
console.dir(countPairs([10, 20, 20, 10, 10, 30, 50, 10, 20]));
console.dir(countPairs([1, 1, 3, 1, 2, 1, 3, 3, 3, 3]));
First argument in your implementation is not necessary.
Please up-vote if answer was helpful
Concise approach with reduce method
const countPairs = arr => (pairs = [], arr.reduce((p, c) => (p[c] ? (pairs.push([p[c], c]), delete p[c]) : p[c] = c, p), {}), pairs.length)
console.log(countPairs([10, 20, 20, 10, 10, 30, 50, 10, 20]));
So, I wanted a more simpler solution to this problem since I'm just starting to learn to code and I'm teaching my self. I found this solution works perfectly for what you want. I didn't created this solution I found it on the internet(https://www.geeksforgeeks.org/count-equal-element-pairs-in-the-given-array/) I just translated it to JavaScript.
function countDuplicates(n, arr) {
var count = 0;
arr.sort();
for (var i = 0; i < n;) {
if (arr[i] === arr[i + 1]) {
count++;
i = i + 2;
} else {
i++;
}
}
return count;
}
console.log(countDuplicates(9, [10, 20, 20, 10, 10, 30, 50, 10, 20]));
There are some more concise answers here, but here's the solution I have for you:
function countDuplicates(arr) {
var counts = {}, sum = 0;
for (var i = 0; i < arr.length; i++) {
counts[arr[i].toString()] = (counts[arr[i].toString()] || 0) + 1;
}
for (var count in counts) {
if (Object.prototype.hasOwnProperty.call(counts, count)) sum += Math.floor(counts[count] / 2);
}
return sum;
}
console.log(countDuplicates([10, 20, 20, 10, 10, 30, 50, 10, 20]));
I hope I have helped
function numberOfPairs(array) {
let arr = [...array].sort();
let result = 0;
for (let i = 0; i < arr.length; i++) {
if (arr[i] == arr[i + 1]) {
result++;
arr.shift();
}
}
console.log(result);
}
numberOfPairs(['blue', 'blue', 'blue', 1, 2, 5, 2, 1]);
Could you please tell me how to find all subarray with sum equal to number
Example
arr[] = [2, 4, 45, 6, 0, 19]
x = 51
Output: [2,4,45]
Or
arr[] = [1, 11, 100, 1, 0, 200, 3, 2, 1, 280]
x = 280
Output: [280]
I tried like that but not getting correct output
function getSubArray(arr, num) {
var sum = 0,
blank = [];
var bigArr = []
for (var i = 0; i < arr.length; i++) {
sum = arr[i];
if (blank.length === 0) {
blank.push(arr[i]);
}
for (var j = 1; i < arr.length; j++) {
sum += arr[j];
if (sum < num) {
blank.push(arr[j])
} else if (sum > num) {
sum = 0;
blank = [];
break;
} else {
blank.push(arr[j])
bigArr.push(blank);
sum = 0;
blank = [];
}
}
}
return bigArr
}
console.log(getSubArray([1, 3, 6, 11, 1, 5, 4], 4));
for this expected output is
console.log(getSubArray([1, 3, 6, 11, 1, 5,4],4));
output: [1,3]
[4]
expected output
[[1,3], [4]] is my expected output
You could iterate the array and take either the next element or if no element is taken before omit this element.
function getSubset(array, sum) {
function iter(temp, delta, index) {
if (!delta) result.push(temp);
if (index >= array.length) return;
iter(temp.concat(array[index]), delta - array[index], index + 1);
if (!temp.length) iter(temp, delta, index + 1);
}
var result = [];
iter([], sum, 0);
return result;
}
console.log(getSubset([2, 4, 45, 6, 0, 19], 51)); // [2, 4, 45], [45, 6], [45, 6, 0]
console.log(getSubset([1, 11, 100, 1, 0, 200, 3, 2, 1, 280], 280)); // [280]
console.log(getSubset([1, 3, 6, 11, 1, 5, 4], 4)); // [1, 3], [4]
This might not be exactly what's needed - might require tweaking as the logic may be flawed here.
I have commented the code for clarification.
var arr = [1, 3, 6, 11, 1, 5,4]; // Define array
var target = 31; // Define target
// filter the numbers higher than target and sort rest ascending
var withinRange = arr.filter(x => x <= target).sort((a, b) => a - b);
if(arr.reduce((a,b) => a + b) < target) // Check if we have enough numbers to make up that number
throw "The max you can get out of your selection is: " + arr.reduce((a,b) => a + b);
// grab the highest number as a starting point and remove it from our array of numbers
var numbers = [withinRange.pop()];
var toFind = target - getSum(); // get remainder to find
for(var i = withinRange.length - 1; i > -1; i--) // iterate from the top
{
if(toFind == withinRange[i]){ // check if number is exactly what we need
numbers.push(withinRange[i]);
break;
}else if(withinRange[i] <= toFind){ // if number is smaller than what we look for
numbers.push(withinRange[i]);
toFind -= withinRange[i];
}
}
function getSum(){ // sum up our found numbers
if(numbers.length == 0) return 0;
return numbers.reduce((a,b) => a + b);
}
console.log([numbers, [target]]); // print numbers as desired output
console.log(target, getSum()) // print the target and our numbers
function combinations(array) {
return new Array(1 << array.length).fill().map(
(e1,i) => array.filter((e2, j) => i & 1 << j));
}
function add(acc,a) {
return acc + a
}
combinations([2, 4, 45, 6, 0, 19]).filter( subarray => subarray.reduce(add, 0) == 51 )
output
[[2,4,45],[45,6],[2,4,45,0],[45,6,0]]
combinations([1, 11, 100, 1, 0, 200, 3, 2, 1, 280]).filter( subarray => subarray.reduce(add, 0) == 280 )
output
[[280],[0,280]]
It will give all the available case. And I use the test case of #Nina Scholz
const sum = arr => arr.reduce((a,b) => a + b)
function cal(arr, x) {
const rs = []
for (let i = 0; i< arr.length; i++) {
const tmp = []
for (let j=i; j<arr.length; j++ ) {
tmp.push(arr[j])
if(sum(tmp) === x) rs.push([...tmp])
}
}
return rs
}
console.log(cal([1, 11, 100, 1, 0, 200, 3, 2, 1, 280], 280)) // -> [280]
console.log(cal([2, 4, 45, 6, 0, 19], 51)); // -> [2, 4, 45] [45, 6] [45, 6, 0]
console.log(cal([1, 3, 6, 11, 1, 5, 4], 4)); // -> [1,3] [4]
This will try every possible permutation of the array (will stop further permutations once limit is reached)
function test(arr, num) {
// sorting will improve time as larger values will be eliminated first
arr = arr.sort(function(a, b) {
return b - a;
});
var allLists = [];
var start = Date.now();
helper(0, 0, []);
console.log("Ms elapesed: " + (Date.now() - start));
return allLists || "Not found";
function helper(start, total, list) {
var result = [];
// Using for loop is faster because you can start from desired index without using filter, slice, splice ...
for (var index = start; index < arr.length; index++) {
var item = arr[index];
// If the total is too large the path can be skipped alltogether
if (total + item <= num) {
// Check lists if number was not included
var test = helper(index + 1, total, list.concat(result)); // remove for efficiency
total += item;
result.push(item);
//if (total === num) index = arr.length; add for efficiency
}
}
if (total === num) allLists.push(list.concat(result));
}
}
console.log(test([2, 4, 45, 6, 0, 19], 51)); // [2,4,45] [2,4,45,0] [6,45] [6,45,0]
console.log(test([1, 11, 100, 1, 0, 200, 3, 2, 1, 280], 280)); // [280] [280,0]
If you want to make it more efficient and just return one of the resulted array just comment out the recursive call. You can also un-comment the line that exits the loop once the limit has been reached (will skip 0s).
If the question is about finding all subsets (rather than subarrays) with the given cross sum it is also known as the perfect sum problem.
https://www.geeksforgeeks.org/perfect-sum-problem-print-subsets-given-sum/
// A recursive function to print all subsets with the
// help of dp[][]. Vector p[] stores current subset.
function printSubsetsRec(arr, i, sum, p)
{
// If we reached end and sum is non-zero. We print
// p[] only if arr[0] is equal to sun OR dp[0][sum]
// is true.
if (i == 0 && sum != 0 && dp[0][sum])
{
p.push(arr[i]);
console.log(p);
return;
}
// If sum becomes 0
if (i == 0 && sum == 0)
{
console.log(p);
return;
}
// If given sum can be achieved after ignoring
// current element.
if (dp[i-1][sum])
{
// Create a new vector to store path
var b = p.slice(0);
printSubsetsRec(arr, i-1, sum, b);
}
// If given sum can be achieved after considering
// current element.
if (sum >= arr[i] && dp[i-1][sum-arr[i]])
{
p.push(arr[i]);
printSubsetsRec(arr, i-1, sum-arr[i], p);
}
}
// Prints all subsets of arr[0..n-1] with sum 0.
function printAllSubsets(arr, sum)
{
var n = arr.length
if (n == 0 || sum < 0)
return;
// Sum 0 can always be achieved with 0 elements
dp = [];
for (var i=0; i<n; ++i)
{
dp[i] = []
dp[i][0] = true;
}
// Sum arr[0] can be achieved with single element
if (arr[0] <= sum)
dp[0][arr[0]] = true;
// Fill rest of the entries in dp[][]
for (var i = 1; i < n; ++i)
for (var j = 0; j < sum + 1; ++j)
dp[i][j] = (arr[i] <= j) ? dp[i-1][j] ||
dp[i-1][j-arr[i]]
: dp[i - 1][j];
if (dp[n-1][sum] == false)
{
console.log("There are no subsets with sum %d\n", sum);
return;
}
// Now recursively traverse dp[][] to find all
// paths from dp[n-1][sum]
var p = [];
printSubsetsRec(arr, n-1, sum, p);
}
printAllSubsets([1,2,3,4,5], 10);
Solution
'use strict';
function print(arr[], i, j) {
let k = 0;
for (k = i; k <= j; k += 1) {
console.log(arr[k]);
}
}
function findSubArrays(arr[], sum) {
let n = arr.length;
let i;
let j;
let sum_so_far;
for (i = 0; i<n; i+= 1) {
sum_so_far = 0;
for (j = i; j < n; j++) {
sum_so_far += arr[j];
if (sum_so_far === sum) {
print(arr, i, j);
}
}
}
}
I would first loop depending on the size of expected arrays.
After that loop for looking for first part of the array which should be filled with positions that will match the desired number.
For example for x= 4 having arr=[5,4,32,8,2,1,2,2,3,4,4]
It would first take the 4's. Output will start on [ [4], [4], [4], ..... ] for positions 1,9,10 (respectively)
Then go for the arrays resulting sum of 2 elements [ ... [2,2], [2,2],[2,2], [1,3] ...] ( positions 4+6, position 4+7 position6+7 and position 5+8)
You would probably want to use another function to sum and check at this point.
Now will do the same for sum of 3 elements (if any) and so on, having max loop set at number of original array (the resulting number could be the sum of all the elements in the array).
The resulting example would be [ [4], [4], [4], [2,2], [2,2],[2,2], [1,3]]
If the elements would be strictly positive, one could collect such subsequences in a single pass, progressing in a worm/caterpillar-like way: stretching its front in order to grow the sum (when it is bellow the target) and contracting its back in order to lower the sum:
function worm(arr,target){
var ret=[];
var head=0;
var tail=0;
var sum=0;
while(head<arr.length){
while(sum<=target && head<arr.length){
sum+=arr[head++];
if(sum===target)
ret.push(arr.slice(tail,head));
}
while(sum>=target && tail<head){
sum-=arr[tail++];
if(sum===target)
ret.push(arr.slice(tail,head));
}
}
return JSON.stringify(arr)+": "+JSON.stringify(ret);
}
console.log(worm([2, 4, 45, 6, 19], 51));
console.log(worm([1, 11, 100, 1, 200, 3, 2, 1, 280], 280));
console.log(worm([1, 3, 6, 11, 1, 5, 4], 4));
console.log("But it only occasionally finds 0+... / ...+0 sums:");
console.log(worm([2, 4, 45, 6, 0, 19], 51));
console.log(worm([2, 4, 0, 45, 0, 6, 0, 19], 51));
console.log(worm([0, 2, 4, 0, 45, 0, 6, 0, 19], 51));
One way to deal with the problem related to bounding zeroes is to throw such sequences away. This snippet keeps tail and head(-1) on non-zero elements:
function worm(arr,target){
var ret=[];
var head=0;
while(head<arr.length && arr[head]===0)head++;
var tail=head;
var sum=0;
while(head<arr.length){
while(sum<=target && head<arr.length){
while(head<arr.length && arr[head]===0)head++;
sum+=arr[head++];
if(sum===target)
ret.push(arr.slice(tail,head));
}
while(sum>=target && tail<head){
sum-=arr[tail++];
while(tail<head && arr[tail]===0)tail++;
if(sum===target)
ret.push(arr.slice(tail,head));
}
}
return JSON.stringify(arr)+": "+JSON.stringify(ret);
}
console.log(worm([2, 4, 45, 6, 19], 51));
console.log(worm([1, 11, 100, 1, 200, 3, 2, 1, 280], 280));
console.log(worm([1, 3, 6, 11, 1, 5, 4], 4));
console.log(worm([2, 4, 45, 6, 0, 19], 51));
console.log(worm([2, 4, 0, 45, 0, 6, 0, 19], 51));
console.log(worm([0, 2, 4, 0, 45, 0, 6, 0, 19], 51));
console.log(worm([0, 2, 4, 0, 45, 0, 6, 0, 19, 26], 51));
console.log(worm([0, 2, 4, 0, 45, 0, 6, 0, 19, 26, 0], 51));
console.log(worm([1,8,2], 10));
console.log(worm([0,1,0,8,2,0], 10));
console.log(worm([0,8,2,8,0], 10));
console.log(worm([0,8,0,2,0,8,0], 10));
And the code loses all of its remaining beauty if someone actually needs those 0+... / ...+0 sequences, as they have to be generated in a post-processing step:
function worm(arr,target){
var pairs=[];
var head=0;
while(head<arr.length && arr[head]===0)head++;
var tail=head;
var sum=0;
while(head<arr.length){
while(sum<=target && head<arr.length){
while(head<arr.length && arr[head]===0)head++;
sum+=arr[head++];
if(sum===target)
pairs.push([tail,head]);
}
while(sum>=target && tail<head){
sum-=arr[tail++];
while(tail<head && arr[tail]===0)tail++;
if(sum===target)
pairs.push([tail,head]);
}
}
var ret=[];
for([tail,head] of pairs){
(function pre(tail,head){
ret.push(arr.slice(tail,head));
if(tail>0 && arr[tail-1]===0)
pre(tail-1,head);
(function post(tail,head){
if(head<arr.length && arr[head]===0){
ret.push(arr.slice(tail,head+1));
post(tail,head+1);
}
})(tail,head);
})(tail,head);
}
return JSON.stringify(arr)+": "+JSON.stringify(ret);
}
console.log(worm([2, 4, 45, 6, 19], 51));
console.log(worm([1, 11, 100, 1, 200, 3, 2, 1, 280], 280));
console.log(worm([1, 3, 6, 11, 1, 5, 4], 4));
console.log(worm([2, 4, 45, 6, 0, 19], 51));
console.log(worm([2, 4, 0, 45, 0, 6, 0, 19], 51));
console.log(worm([0, 2, 4, 0, 45, 0, 6, 0, 19], 51));
console.log(worm([0, 2, 4, 0, 45, 0, 6, 0, 19, 26], 51));
console.log(worm([0, 2, 4, 0, 45, 0, 6, 0, 19, 26, 0], 51));
console.log(worm([1,8,2], 10));
console.log(worm([0,1,0,8,2,0], 10));
console.log(worm([0,8,2,8,0], 10));
console.log(worm([0,8,0,2,0,8,0], 10));
I think it works (for non-negative elements), but the first one was simpler for sure.
With map and filter
const arr = [2, 4, 45, 6, 0, 19]
let t = 0
const result = arr.map((v,i)=>{
return [v, t += v]
}).filter((v,i)=>v[1]<=51)
console.log(result)
I can do this with three loops but complexity will be O(n3), can it be done with less complexity?
Adding js fiddle code for three loops approach
var arr = [1, 2, 3, 4, 5, 6, 7, 8];
var sum = 8;
find(arr, sum);
function find(arr, sum) {
var found = false;
for (var x = 0; x < arr.length - 3; x++) {
for (var y = x + 1; y < arr.length - 2; y++) {
for (var z = y + 1; z < arr.length; z++) {
if (arr[x] + arr[y] + arr[z] == sum) {
found = true;
break;
}
}
}
}
if (found) {
console.log("found");
} else {
console.log("not found");
}
}
You could use a hash table and one loop for the array and one for the sum of two items.
function find(array, sum) {
var hash = Object.create(null);
return array.some((v, i, a) => {
a.slice(0, i).forEach(w => hash[v + w] = true);
return hash[sum - v];
});
}
console.log(find([1, 2, 3, 4, 5, 6, 7, 8], 8));
console.log(find([1, 2, 3, 4, 5, 6, 7, 8], 42));
If you like it more functional with a closure over a Set for the sum two values, then this would work, too.
function find(array, sum) {
return array.some(
(s => (v, i, a) => a.slice(0, i).reduce((t, w) => t.add(v + w), s).has(sum - v))
(new Set)
);
}
console.log(find([1, 2, 3, 4, 5, 6, 7, 8], 8));
console.log(find([1, 2, 3, 4, 5, 6, 7, 8], 42));
I am not sure if title is correct.
I have few labels which have set their positions in y scale in range:
range = [0, 100px]
for example: 5 labels in positions:
positions = [5px, 6px, 8px, 72px, 76px]
Now I want my algorithm to correct these positions to not let them be closer than 10px to each other and do minimal corrections.
I am expecting calling my function like this:
result = calculateNewPositions(range, positions, min(10px, 100px / positions.length))
and result in this case should be:
[0px, 10px, 20px, 69px, 79px]
What is name of this alghoritm or how to implement that?
Here's an algorithm that should work pretty well for most cases, and tries to make as minimal amount of adjustments as necessary from the original values.
Iterate through each pair of elements.
If the space is not large enough, move them apart from each other by 1, making sure not to violate the range.
Repeat until all the elements have enough space between them.
And here is a sample implementation:
function calculateNewPositions(positions, minSpacing, rangeMin, rangeMax) {
var temp = positions.slice(0);
var madeChange;
do {
madeChange = false;
for (var i = 0; i < temp.length - 1; i++)
if (temp[i + 1] - temp[i] < minSpacing) {
if (temp[i] > rangeMin) { temp[i]--; madeChange = true; }
if (temp[i + 1] < rangeMax) { temp[i + 1]++; madeChange = true; }
}
} while (madeChange);
return temp;
}
Demo: https://jsfiddle.net/aaxmuw2t/
Example Result: [0, 10, 20, 69, 79]
Note that this algorithm is very simplistic and may not always yield the best result for really complex arrays with lots of close numbers. For example, if you input [33, 34, 35, 36], you get [19, 29, 40, 50], which has an extra unnecessary space.
calculateNewPositions = function(positions, minDelta) {
var newPositions = [0]
positions.slice(1).forEach(function(pos, index) {
var delta = positions[index + 1] - positions[index]
newPositions.push(newPositions[index] + Math.max(delta, minDelta))
})
return newPositions
}
https://tonicdev.com/lipp/pos-diff
I finally did something like this:
var fixPositions = function(range, pos, delta, strict) {
var i;
var leftSpaces = [];
var halfDelta = strict ? delta / 2 : 0;
delta = Math.min(delta, (range[1] - range[0] / (pos.length + (strict ? 0 : 1))));
// calculate all spaces that are greater than delta
leftSpaces.push(Math.max(pos[0] - range[0] - halfDelta, 0));
for (i = 1; i < pos.length; i++) {
leftSpaces.push(Math.max(pos[i] - pos[i-1] - delta, 0));
}
leftSpaces.push(Math.max(range[1] - pos[pos.length-1] - halfDelta, 0));
// save indexes of big spaces
var nonZeroSpacesIdx = [];
leftSpaces.map(function(space, i) {
if (space > 0) {
nonZeroSpacesIdx.push(i);
}
});
// sort indexes by spaces sizes (start from smaller)
nonZeroSpacesIdx.sort(function(a, b) {
return leftSpaces[a] - leftSpaces[b];
});
// loop until spaces sum are greater than range
var spacesSum = Infinity;
while (nonZeroSpacesIdx.length > 0 && spacesSum > 0) {
spacesSum = 0;
for (i = 0; i < nonZeroSpacesIdx.length; i++) {
spacesSum += leftSpaces[nonZeroSpacesIdx[i]];
}
var missingDiff = (spacesSum + (pos.length - 1) * delta + halfDelta * 2) - (range[1] - range[0]);
if (missingDiff <= 0) {
break;
}
// find min diff which can be substracted from all spaces
var minDiff = Math.min(missingDiff / nonZeroSpacesIdx.length, leftSpaces[nonZeroSpacesIdx[0]]);
for (i = 0; i < nonZeroSpacesIdx.length; i++) {
leftSpaces[nonZeroSpacesIdx[i]] -= minDiff;
}
// remove index of first space if its equal zero
if (leftSpaces[nonZeroSpacesIdx[0]] <= 0) {
nonZeroSpacesIdx.shift();
}
}
// reconstruct new positions
var newPos = [];
newPos.push(range[0] + leftSpaces[0] + halfDelta);
for (i = 1; i < leftSpaces.length - 1; i++) {
newPos[i] = newPos[i-1] + leftSpaces[i] + delta;
}
return newPos;
};
// result should be from range: [5, 95]
console.log(fixPositions([0, 100], [5, 6, 8, 72, 76], 10, true));
// result should be from range: [0, 100]
console.log(fixPositions([0, 100], [5, 6, 8, 72, 76], 10, false));
https://jsfiddle.net/fcwu1oyu/14/
Its not giving exact same values for my input, but it does the job for my pie charts:
A work in progress solution
The code pushes two too close couples appart with one on each side. This is symetrically and results in sometimes to far pushed values, which can be corrected.
function disperse(values, threshold, range) {
var delta = Array.apply(null, { length: values.length }).map(function () { return 0; }),
converged = false;
while (!converged) {
converged = true;
delta = delta.map(function (d, i, dd) {
if (i < dd.length - 1 && dd.length > 1 && values[i + 1] + dd[i + 1] - values[i] - d < threshold) {
converged = false;
dd[i + 1] += 1;
return d - 1;
}
return d;
});
}
converged = false;
// try to minimise difference
while (!converged) {
converged = true;
delta = delta.map(function (d, i) {
var space;
if (i < delta.length - 2) {
space = values[i + 1] + delta[i + 1] - values[i] - d;
if (d < 0 && space > threshold) {
converged = false;
return d + space - threshold;
}
}
return d;
});
}
// respect lower range
delta.reduce(function (r, d, i, dd) {
if (values[i] + d < r) {
dd[i] = r - values[i];
return r + threshold;
}
return values[i] + threshold + d;
}, range[0]);
// respect upper range
delta.reduceRight(function (r, d, i, dd) {
if (values[i] + d > r) {
dd[i] = r - values[i];
return r - threshold;
}
return values[i] + d;
}, range[1]);
return values.map(function (v, i) {
return v + delta[i];
});
}
document.write('<pre>' + JSON.stringify(disperse([5, 6, 8, 72, 76], 10, [0, 100]), 0, 4) + '</pre>');
document.write('<pre>' + JSON.stringify(disperse([5, 6, 7, 8, 72, 76], 10, [0, 100]), 0, 4) + '</pre>');
document.write('<pre>' + JSON.stringify(disperse([24, 28, 92, 94, 95], 10, [0, 100]), 0, 4) + '</pre>');
In PHP, you can do...
range(1, 3); // Array(1, 2, 3)
range("A", "C"); // Array("A", "B", "C")
That is, there is a function that lets you get a range of numbers or characters by passing the upper and lower bounds.
Is there anything built-in to JavaScript natively for this? If not, how would I implement it?
Numbers
[...Array(5).keys()];
=> [0, 1, 2, 3, 4]
Character iteration
String.fromCharCode(...[...Array('D'.charCodeAt(0) - 'A'.charCodeAt(0) + 1).keys()].map(i => i + 'A'.charCodeAt(0)));
=> "ABCD"
Iteration
for (const x of Array(5).keys()) {
console.log(x, String.fromCharCode('A'.charCodeAt(0) + x));
}
=> 0,"A" 1,"B" 2,"C" 3,"D" 4,"E"
As functions
function range(size, startAt = 0) {
return [...Array(size).keys()].map(i => i + startAt);
}
function characterRange(startChar, endChar) {
return String.fromCharCode(...range(endChar.charCodeAt(0) -
startChar.charCodeAt(0), startChar.charCodeAt(0)))
}
As typed functions
function range(size:number, startAt:number = 0):ReadonlyArray<number> {
return [...Array(size).keys()].map(i => i + startAt);
}
function characterRange(startChar:string, endChar:string):ReadonlyArray<string> {
return String.fromCharCode(...range(endChar.charCodeAt(0) -
startChar.charCodeAt(0), startChar.charCodeAt(0)))
}
lodash.js _.range() function
_.range(10);
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
_.range(1, 11);
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
_.range(0, 30, 5);
=> [0, 5, 10, 15, 20, 25]
_.range(0, -10, -1);
=> [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
String.fromCharCode(..._.range('A'.charCodeAt(0), 'D'.charCodeAt(0) + 1));
=> "ABCD"
Old non es6 browsers without a library:
Array.apply(null, Array(5)).map(function (_, i) {return i;});
=> [0, 1, 2, 3, 4]
console.log([...Array(5).keys()]);
(ES6 credit to nils petersohn and other commenters)
For numbers you can use ES6 Array.from(), which works in everything these days except IE:
Shorter version:
Array.from({length: 20}, (x, i) => i);
Longer version:
Array.from(new Array(20), (x, i) => i);
which creates an array from 0 to 19 inclusive. This can be further shortened to one of these forms:
Array.from(Array(20).keys());
// or
[...Array(20).keys()];
Lower and upper bounds can be specified too, for example:
Array.from(new Array(20), (x, i) => i + *lowerBound*);
An article describing this in more detail: http://www.2ality.com/2014/05/es6-array-methods.html
My new favorite form (ES2015)
Array(10).fill(1).map((x, y) => x + y)
And if you need a function with a step param:
const range = (start, stop, step = 1) =>
Array(Math.ceil((stop - start) / step)).fill(start).map((x, y) => x + y * step)
Another possible implementation suggested by the MDN docs:
// Sequence generator function
// (commonly referred to as "range", e.g. Clojure, PHP etc)
const range = (start, stop, step) =>
Array.from({ length: (stop - start) / step + 1}, (_, i) => start + (i * step))
Here's my 2 cents:
function range(start, end) {
return Array.apply(0, Array(end - 1))
.map((element, index) => index + start);
}
It works for characters and numbers, going forwards or backwards with an optional step.
var range = function(start, end, step) {
var range = [];
var typeofStart = typeof start;
var typeofEnd = typeof end;
if (step === 0) {
throw TypeError("Step cannot be zero.");
}
if (typeofStart == "undefined" || typeofEnd == "undefined") {
throw TypeError("Must pass start and end arguments.");
} else if (typeofStart != typeofEnd) {
throw TypeError("Start and end arguments must be of same type.");
}
typeof step == "undefined" && (step = 1);
if (end < start) {
step = -step;
}
if (typeofStart == "number") {
while (step > 0 ? end >= start : end <= start) {
range.push(start);
start += step;
}
} else if (typeofStart == "string") {
if (start.length != 1 || end.length != 1) {
throw TypeError("Only strings with one character are supported.");
}
start = start.charCodeAt(0);
end = end.charCodeAt(0);
while (step > 0 ? end >= start : end <= start) {
range.push(String.fromCharCode(start));
start += step;
}
} else {
throw TypeError("Only string and number types are supported");
}
return range;
}
jsFiddle.
If augmenting native types is your thing, then assign it to Array.range.
var range = function(start, end, step) {
var range = [];
var typeofStart = typeof start;
var typeofEnd = typeof end;
if (step === 0) {
throw TypeError("Step cannot be zero.");
}
if (typeofStart == "undefined" || typeofEnd == "undefined") {
throw TypeError("Must pass start and end arguments.");
} else if (typeofStart != typeofEnd) {
throw TypeError("Start and end arguments must be of same type.");
}
typeof step == "undefined" && (step = 1);
if (end < start) {
step = -step;
}
if (typeofStart == "number") {
while (step > 0 ? end >= start : end <= start) {
range.push(start);
start += step;
}
} else if (typeofStart == "string") {
if (start.length != 1 || end.length != 1) {
throw TypeError("Only strings with one character are supported.");
}
start = start.charCodeAt(0);
end = end.charCodeAt(0);
while (step > 0 ? end >= start : end <= start) {
range.push(String.fromCharCode(start));
start += step;
}
} else {
throw TypeError("Only string and number types are supported");
}
return range;
}
console.log(range("A", "Z", 1));
console.log(range("Z", "A", 1));
console.log(range("A", "Z", 3));
console.log(range(0, 25, 1));
console.log(range(0, 25, 5));
console.log(range(20, 5, 5));
Simple range function:
function range(start, stop, step) {
var a = [start], b = start;
while (b < stop) {
a.push(b += step || 1);
}
return a;
}
To incorporate the BigInt data type some check can be included, ensuring that all variables are same typeof start:
function range(start, stop, step) {
var a = [start], b = start;
if (typeof start == 'bigint') {
stop = BigInt(stop)
step = step? BigInt(step): 1n;
} else
step = step || 1;
while (b < stop) {
a.push(b += step);
}
return a;
}
To remove values higher than defined by stop e.g. range(0,5,2) will include 6, which shouldn't be.
function range(start, stop, step) {
var a = [start], b = start;
while (b < stop) {
a.push(b += step || 1);
}
return (b > stop) ? a.slice(0,-1) : a;
}
OK, in JavaScript we don't have a range() function like PHP, so we need to create the function which is quite easy thing, I write couple of one-line functions for you and separate them for Numbers and Alphabets as below:
for Numbers:
function numberRange (start, end) {
return new Array(end - start).fill().map((d, i) => i + start);
}
and call it like:
numberRange(5, 10); //[5, 6, 7, 8, 9]
for Alphabets:
function alphabetRange (start, end) {
return new Array(end.charCodeAt(0) - start.charCodeAt(0)).fill().map((d, i) => String.fromCharCode(i + start.charCodeAt(0)));
}
and call it like:
alphabetRange('c', 'h'); //["c", "d", "e", "f", "g"]
Array.range = function(a, b, step){
var A = [];
if(typeof a == 'number'){
A[0] = a;
step = step || 1;
while(a+step <= b){
A[A.length]= a+= step;
}
}
else {
var s = 'abcdefghijklmnopqrstuvwxyz';
if(a === a.toUpperCase()){
b = b.toUpperCase();
s = s.toUpperCase();
}
s = s.substring(s.indexOf(a), s.indexOf(b)+ 1);
A = s.split('');
}
return A;
}
Array.range(0,10);
// [0,1,2,3,4,5,6,7,8,9,10]
Array.range(-100,100,20);
// [-100,-80,-60,-40,-20,0,20,40,60,80,100]
Array.range('A','F');
// ['A','B','C','D','E','F')
Array.range('m','r');
// ['m','n','o','p','q','r']
https://stackoverflow.com/a/49577331/8784402
With Delta/Step
smallest and one-liner
[...Array(N)].map((_, i) => from + i * step);
Examples and other alternatives
[...Array(10)].map((_, i) => 4 + i * 2);
//=> [4, 6, 8, 10, 12, 14, 16, 18, 20, 22]
Array.from(Array(10)).map((_, i) => 4 + i * 2);
//=> [4, 6, 8, 10, 12, 14, 16, 18, 20, 22]
Array.from(Array(10).keys()).map(i => 4 + i * 2);
//=> [4, 6, 8, 10, 12, 14, 16, 18, 20, 22]
[...Array(10).keys()].map(i => 4 + i * -2);
//=> [4, 2, 0, -2, -4, -6, -8, -10, -12, -14]
Array(10).fill(0).map((_, i) => 4 + i * 2);
//=> [4, 6, 8, 10, 12, 14, 16, 18, 20, 22]
Array(10).fill().map((_, i) => 4 + i * -2);
//=> [4, 2, 0, -2, -4, -6, -8, -10, -12, -14]
Range Function
const range = (from, to, step) =>
[...Array(Math.floor((to - from) / step) + 1)].map((_, i) => from + i * step);
range(0, 9, 2);
//=> [0, 2, 4, 6, 8]
// can also assign range function as static method in Array class (but not recommended )
Array.range = (from, to, step) =>
[...Array(Math.floor((to - from) / step) + 1)].map((_, i) => from + i * step);
Array.range(2, 10, 2);
//=> [2, 4, 6, 8, 10]
Array.range(0, 10, 1);
//=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Array.range(2, 10, -1);
//=> []
Array.range(3, 0, -1);
//=> [3, 2, 1, 0]
As Iterators
class Range {
constructor(total = 0, step = 1, from = 0) {
this[Symbol.iterator] = function* () {
for (let i = 0; i < total; yield from + i++ * step) {}
};
}
}
[...new Range(5)]; // Five Elements
//=> [0, 1, 2, 3, 4]
[...new Range(5, 2)]; // Five Elements With Step 2
//=> [0, 2, 4, 6, 8]
[...new Range(5, -2, 10)]; // Five Elements With Step -2 From 10
//=>[10, 8, 6, 4, 2]
[...new Range(5, -2, -10)]; // Five Elements With Step -2 From -10
//=> [-10, -12, -14, -16, -18]
// Also works with for..of loop
for (i of new Range(5, -2, 10)) console.log(i);
// 10 8 6 4 2
As Generators Only
const Range = function* (total = 0, step = 1, from = 0) {
for (let i = 0; i < total; yield from + i++ * step) {}
};
Array.from(Range(5, -2, -10));
//=> [-10, -12, -14, -16, -18]
[...Range(5, -2, -10)]; // Five Elements With Step -2 From -10
//=> [-10, -12, -14, -16, -18]
// Also works with for..of loop
for (i of Range(5, -2, 10)) console.log(i);
// 10 8 6 4 2
// Lazy loaded way
const number0toInf = Range(Infinity);
number0toInf.next().value;
//=> 0
number0toInf.next().value;
//=> 1
// ...
From-To with steps/delta
using iterators
class Range2 {
constructor(to = 0, step = 1, from = 0) {
this[Symbol.iterator] = function* () {
let i = 0,
length = Math.floor((to - from) / step) + 1;
while (i < length) yield from + i++ * step;
};
}
}
[...new Range2(5)]; // First 5 Whole Numbers
//=> [0, 1, 2, 3, 4, 5]
[...new Range2(5, 2)]; // From 0 to 5 with step 2
//=> [0, 2, 4]
[...new Range2(5, -2, 10)]; // From 10 to 5 with step -2
//=> [10, 8, 6]
using Generators
const Range2 = function* (to = 0, step = 1, from = 0) {
let i = 0,
length = Math.floor((to - from) / step) + 1;
while (i < length) yield from + i++ * step;
};
[...Range2(5, -2, 10)]; // From 10 to 5 with step -2
//=> [10, 8, 6]
let even4to10 = Range2(10, 2, 4);
even4to10.next().value;
//=> 4
even4to10.next().value;
//=> 6
even4to10.next().value;
//=> 8
even4to10.next().value;
//=> 10
even4to10.next().value;
//=> undefined
For Typescript
class _Array<T> extends Array<T> {
static range(from: number, to: number, step: number): number[] {
return Array.from(Array(Math.floor((to - from) / step) + 1)).map(
(v, k) => from + k * step
);
}
}
_Array.range(0, 9, 1);
https://stackoverflow.com/a/64599169/8784402
Generate Character List with one-liner
const charList = (a,z,d=1)=>(a=a.charCodeAt(),z=z.charCodeAt(),[...Array(Math.floor((z-a)/d)+1)].map((_,i)=>String.fromCharCode(a+i*d)));
console.log("from A to G", charList('A', 'G'));
console.log("from A to Z with step/delta of 2", charList('A', 'Z', 2));
console.log("reverse order from Z to P", charList('Z', 'P', -1));
console.log("from 0 to 5", charList('0', '5', 1));
console.log("from 9 to 5", charList('9', '5', -1));
console.log("from 0 to 8 with step 2", charList('0', '8', 2));
console.log("from α to ω", charList('α', 'ω'));
console.log("Hindi characters from क to ह", charList('क', 'ह'));
console.log("Russian characters from А to Я", charList('А', 'Я'));
For TypeScript
const charList = (p: string, q: string, d = 1) => {
const a = p.charCodeAt(0),
z = q.charCodeAt(0);
return [...Array(Math.floor((z - a) / d) + 1)].map((_, i) =>
String.fromCharCode(a + i * d)
);
};
var range = (l,r) => new Array(r - l).fill().map((_,k) => k + l);
Handy function to do the trick, run the code snippet below
function range(start, end, step, offset) {
var len = (Math.abs(end - start) + ((offset || 0) * 2)) / (step || 1) + 1;
var direction = start < end ? 1 : -1;
var startingPoint = start - (direction * (offset || 0));
var stepSize = direction * (step || 1);
return Array(len).fill(0).map(function(_, index) {
return startingPoint + (stepSize * index);
});
}
console.log('range(1, 5)=> ' + range(1, 5));
console.log('range(5, 1)=> ' + range(5, 1));
console.log('range(5, 5)=> ' + range(5, 5));
console.log('range(-5, 5)=> ' + range(-5, 5));
console.log('range(-10, 5, 5)=> ' + range(-10, 5, 5));
console.log('range(1, 5, 1, 2)=> ' + range(1, 5, 1, 2));
here is how to use it
range (Start, End, Step=1, Offset=0);
inclusive - forward range(5,10) // [5, 6, 7, 8, 9, 10]
inclusive - backward range(10,5) // [10, 9, 8, 7, 6, 5]
step - backward range(10,2,2) // [10, 8, 6, 4, 2]
exclusive - forward range(5,10,0,-1) // [6, 7, 8, 9] not 5,10 themselves
offset - expand range(5,10,0,1) // [4, 5, 6, 7, 8, 9, 10, 11]
offset - shrink range(5,10,0,-2) // [7, 8]
step - expand range(10,0,2,2) // [12, 10, 8, 6, 4, 2, 0, -2]
hope you find it useful.
And here is how it works.
Basically I'm first calculating the length of the resulting array and create a zero filled array to that length, then fill it with the needed values
(step || 1) => And others like this means use the value of step and if it was not provided use 1 instead
We start by calculating the length of the result array using (Math.abs(end - start) + ((offset || 0) * 2)) / (step || 1) + 1) to put it simpler (difference* offset in both direction/step)
After getting the length, then we create an empty array with initialized values using new Array(length).fill(0); check here
Now we have an array [0,0,0,..] to the length we want. We map over it and return a new array with the values we need by using Array.map(function() {})
var direction = start < end ? 1 : 0; Obviously if start is not smaller than the end we need to move backward. I mean going from 0 to 5 or vice versa
On every iteration, startingPoint + stepSize * index will gives us the value we need
--- UPDATE (Thanks to #lokhmakov for simplification) ---
Another version using ES6 generators ( see great Paolo Moretti answer with ES6 generators ):
const RANGE = (x,y) => Array.from((function*(){
while (x <= y) yield x++;
})());
console.log(RANGE(3,7)); // [ 3, 4, 5, 6, 7 ]
Or, if we only need iterable, then:
const RANGE_ITER = (x,y) => (function*(){
while (x <= y) yield x++;
})();
for (let n of RANGE_ITER(3,7)){
console.log(n);
}
// 3
// 4
// 5
// 6
// 7
--- ORGINAL code was: ---
const RANGE = (a,b) => Array.from((function*(x,y){
while (x <= y) yield x++;
})(a,b));
and
const RANGE_ITER = (a,b) => (function*(x,y){
while (x <= y) yield x++;
})(a,b);
Using Harmony spread operator and arrow functions:
var range = (start, end) => [...Array(end - start + 1)].map((_, i) => start + i);
Example:
range(10, 15);
[ 10, 11, 12, 13, 14, 15 ]
If, on Visual Studio Code, you faced the error:
Type 'IterableIterator' is not an array type or a string type. Use compiler option '--downlevelIteration' to allow iterating of iterators.
Instead of
[...Array(3).keys()]
you can rely on
Array.from(Array(3).keys())
More on downlevelIteration
You can use lodash or Undescore.js range:
var range = require('lodash/range')
range(10)
// -> [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
Alternatively, if you only need a consecutive range of integers you can do something like:
Array.apply(undefined, { length: 10 }).map(Number.call, Number)
// -> [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
In ES6 range can be implemented with generators:
function* range(start=0, end=null, step=1) {
if (end == null) {
end = start;
start = 0;
}
for (let i=start; i < end; i+=step) {
yield i;
}
}
This implementation saves memory when iterating large sequences, because it doesn't have to materialize all values into an array:
for (let i of range(1, oneZillion)) {
console.log(i);
}
Did some research on some various Range Functions.
Checkout the jsperf comparison of the different ways to do these functions. Certainly not a perfect or exhaustive list, but should help :)
The Winner is...
function range(lowEnd,highEnd){
var arr = [],
c = highEnd - lowEnd + 1;
while ( c-- ) {
arr[c] = highEnd--
}
return arr;
}
range(0,31);
Technically its not the fastest on firefox, but crazy speed difference (imho) on chrome makes up for it.
Also interesting observation is how much faster chrome is with these array functions than firefox. Chrome is at least 4 or 5 times faster.
range(start,end,step): With ES6 Iterators
You only ask for an upper and lower bounds. Here we create one with a step too.
You can easily create range() generator function which can function as an iterator. This means you don't have to pre-generate the entire array.
function * range ( start, end, step = 1 ) {
let state = start;
while ( state < end ) {
yield state;
state += step;
}
return;
};
Now you may want to create something that pre-generates the array from the iterator and returns a list. This is useful for functions that accept an array. For this we can use Array.from()
const generate_array = (start,end,step) =>
Array.from( range(start,end,step) );
Now you can generate a static array easily,
const array1 = generate_array(1,10,2);
const array1 = generate_array(1,7);
But when something desires an iterator (or gives you the option to use an iterator) you can easily create one too.
for ( const i of range(1, Number.MAX_SAFE_INTEGER, 7) ) {
console.log(i)
}
Special Notes
If you use Ramda, they have their own R.range as does Lodash
This may not be the best way. But if you are looking to get a range of numbers in a single line of code. For example 10 - 50
Array(40).fill(undefined).map((n, i) => i + 10)
Where 40 is (end - start) and 10 is the start. This should return [10, 11, ..., 50]
Not implemented yet!
Using the new Number.range proposal (stage 1):
[...Number.range(1, 10)]
//=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
(from, to) => [...Array(to - from)].map((_,i)=> i + from)
An interesting challenge would be to write the shortest function to do this. Recursion to the rescue!
function r(a,b){return a>b?[]:[a].concat(r(++a,b))}
Tends to be slow on large ranges, but luckily quantum computers are just around the corner.
An added bonus is that it's obfuscatory. Because we all know how important it is to hide our code from prying eyes.
To truly and utterly obfuscate the function, do this:
function r(a,b){return (a<b?[a,b].concat(r(++a,--b)):a>b?[]:[a]).sort(function(a,b){return a-b})}
I would code something like this:
function range(start, end) {
return Array(end-start).join(0).split(0).map(function(val, id) {return id+start});
}
range(-4,2);
// [-4,-3,-2,-1,0,1]
range(3,9);
// [3,4,5,6,7,8]
It behaves similarly to Python range:
>>> range(-4,2)
[-4, -3, -2, -1, 0, 1]
My personal favorite:
const range = (start, end) => new Array(end-start+1).fill().map((el, ind) => ind + start);
ES6
Use Array.from (docs here):
const range = (start, stop, step) => Array.from({ length: (stop - start) / step + 1}, (_, i) => start + (i * step));
A rather minimalistic implementation that heavily employs ES6 can be created as follows, drawing particular attention to the Array.from() static method:
const getRange = (start, stop) => Array.from(
new Array((stop - start) + 1),
(_, i) => i + start
);
The standard Javascript doesn't have a built-in function to generate ranges. Several javascript frameworks add support for such features, or as others have pointed out you can always roll your own.
If you'd like to double-check, the definitive resource is the ECMA-262 Standard.
Though this is not from PHP, but an imitation of range from Python.
function range(start, end) {
var total = [];
if (!end) {
end = start;
start = 0;
}
for (var i = start; i < end; i += 1) {
total.push(i);
}
return total;
}
console.log(range(10)); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
console.log(range(0, 10)); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
console.log(range(5, 10)); // [5, 6, 7, 8, 9]
This one works also in reverse.
const range = ( a , b ) => Array.from( new Array( b > a ? b - a : a - b ), ( x, i ) => b > a ? i + a : a - i );
range( -3, 2 ); // [ -3, -2, -1, 0, 1 ]
range( 1, -4 ); // [ 1, 0, -1, -2, -3 ]
As far as generating a numeric array for a given range, I use this:
function range(start, stop)
{
var array = [];
var length = stop - start;
for (var i = 0; i <= length; i++) {
array[i] = start;
start++;
}
return array;
}
console.log(range(1, 7)); // [1,2,3,4,5,6,7]
console.log(range(5, 10)); // [5,6,7,8,9,10]
console.log(range(-2, 3)); // [-2,-1,0,1,2,3]
Obviously, it won't work for alphabetical arrays.
You can use following one-liner to keep things short and simple
var start = 4;
var end = 20;
console.log(Array(end - start + 1).fill(start).map((x, y) => x + y));