Calculate Conway's Constant - javascript

I found a code golf challenge that requires you to calculate Conway's Constant to the first 1000 digits. The problem is I couldn't find any place that tells how to calculate this, just websites showing polynomials with a variable x that I do not know what is.
I calculated the first 30 numbers in the Look-and-say sequence with this code:
const nums = ["1"],
trailingSequences = seq => {
const num = seq[0];
let counter = 1;
let idx = 0;
for (let i = 1; i < seq; i++) {
if (num == seq[i]) {
counter++
idx = i;
} else {
break
};
}
return [`${counter}${num}`, idx + 1];
},
getNext = previous => {
let next = "";
while (true) {
if (previous == "") {
break
};
const part = trailingSequences(previous);
next += part[0];
previous = previous.slice(part[1]);
}
return next;
}
for (let i = 0; i < 30; i++)
nums.push(getNext(nums[nums.length - 1]))
console.log(nums.join("\n\n\n"));
But I still do not know how to extract Conway's constant regardless.
So, how to calculate Conway's Constant to a modifiable precision in JavaScript?

Conway's Constant is the unique real positive root of an order-71 polynomial. As you might expect it is irrational1, and cannot be expressed as a finite continued fraction.
One of the easiest, and generally speaking most efficient methods to compute polynomial roots is with Newton's method:
where xn is the current guess and f(xn) is a function that evaluates the target polynomial at xn.
In the case that arbitrary precision reals/rationals are not available, the result can instead be scaled by a large power of 10 to compute the desired precision. The implementation below uses V8's BigInts to compute Conway's Constant to 1000 places.
/**
* Evaluates a polynomial given by coeffs at x,
* or the derivative thereof, scaled by a factor.
*/
function evalpoly(x, coeffs, scale, deriv) {
let ret = 0n;
const d = deriv ? 1 : 0;
for(let i = coeffs.length - 1; i >= d; i--) {
ret = x*ret / scale + BigInt(coeffs[i] * [1, i][d]) * scale;
}
return ret;
}
const poly = [
-6, 3, -6, 12, -4, 7, -7, 1, 0, 5, -2, -4, -12, 2, 7, 12, -7, -10,
-4, 3, 9, -7, 0, -8, 14, -3, 9, 2, -3, -10, -2, -6, 1, 10, -3, 1,
7, -7, 7, -12, -5, 8, 6, 10, -8, -8, -7, -3, 9, 1, 6, 6, -2, -3,
-10, -2, 3, 5, 2, -1, -1, -1, -1, -1, 1, 2, 2, -1, -2, -1, 0, 1]
const scale = 10n**1000n
// initial guess 1.333333...
let x = scale * 4n / 3n
for(let i = 0; i < 14; i++) {
x -= evalpoly(x, poly, scale, 0) * scale / evalpoly(x, poly, scale, 1)
}
x
1 Finch, Steven R., Mathematical Constants, pp. 642, Cambridge University Press, 2003.

The Conway Constant is the ratio between the length of digits of n and n-1 as n approaches inf.
const nums = ["1"],
trailingSequences = seq => {
const num = seq[0];
let counter = 1;
let idx = 0;
for (let i = 1; i < seq; i++) {
if (num == seq[i]) {
counter++
idx = i;
} else {
break
};
}
return [`${counter}${num}`, idx + 1];
},
getNext = previous => {
let next = "";
while (true) {
if (previous == "") {
break
};
const part = trailingSequences(previous);
next += part[0];
previous = previous.slice(part[1]);
}
return next;
}
for (let i = 0; i < 30; i++){
let prev = (nums[nums.length - 1] + '').length;
let current = (getNext(nums[nums.length - 1]) + '').length;
nums.push(getNext(nums[nums.length - 1]))
// ratio of n / n-1, this is the approx of Conway's Constant
console.log(current / prev);
}
//console.log(nums.join("\n\n\n"));

Related

Identify longest period of variance in an array of integers

I'm trying to write a function that will identify the longest period of variance in an array of numbers. Variance begins when the previous number is higher than the current, and ends when the next number is the same as the current; however, if variance doesn't end, then it is assumed the variance began with the last two numbers.
For example: [10, 5, 3, 11, 8, 9, 9, 2, 10] The longest period of variance in this array is [5, 3, 11, 8, 9], or just 5 (the length). A variance ends when the following number is the same as the current, in this case, 9.
The function I've written works on this case; however, it doesn't when the entire array has variance, such as [10, 5, 10, 5, 10, 5, 10, 5, 10], which returns 8, when it should be 9.
In the case where the previous number is number is always lower, or always higher than the variance would be 2, because it never ended. For example [2, 4, 6, 8] and [8, 6, 4, 2].
I know the issue with the entire array variance can be solved by starting the for loop at 0, but then the other cases become invalid. Any help is greatly appreciated.
Without further ado, here is my code:
function findVariance(numbers) {
if ([0,1].includes(numbers.length)) return numbers.length;
const variance = [[0]];
let greater = numbers[1] > numbers[0];
let lesser = numbers[1] < numbers[0];
for (let i = 1; i < numbers.length; i++) {
let previous = variance.length - 1;
let previousVarianceGroup = variance[previous];
let previousVarianceGroupValue = previousVarianceGroup[previousVarianceGroup.length - 1];
if (greater) {
if (numbers[i] < numbers[previousVarianceGroupValue]) {
previousVarianceGroup.push(i);
greater = false;
lesser = true;
} else {
greater = numbers[i] < numbers[previousVarianceGroupValue];
lesser = numbers[i] < numbers[previousVarianceGroupValue];
variance.push([previousVarianceGroupValue, i]);
}
} else if (lesser) {
if (numbers[i] > numbers[previousVarianceGroupValue]) {
previousVarianceGroup.push(i);
greater = true;
lesser = false;
} else {
greater = numbers[i] > numbers[previousVarianceGroupValue];
lesser = numbers[i] > numbers[previousVarianceGroupValue];
variance.push([previousVarianceGroupValue, i]);
}
} else {
greater = numbers[i] > numbers[previousVarianceGroupValue];
lesser = numbers[i] < numbers[previousVarianceGroupValue];
variance.push([previousVarianceGroupValue, i]);
}
}
const result = [];
for (let i = 0; i < variance.length; i++) {
result[i] = variance[i].length;
}
result.sort();
return result[result.length - 1];
}
console.log(findVariance([10, 5, 3, 11, 8, 9, 9, 2, 10]));
console.log(findVariance([10, 5, 10, 5, 10, 5, 10, 5, 10]));
console.log(findVariance([2, 4, 6, 8]));
Here's what I got (trying to understand the question as best i could)
function calculateVariance(arr) {
// trivial cases
if (arr.length <= 1) { return arr.length; }
// store the difference between each pair of adjacent numbers
let diffs = [];
for (let i = 1; i < arr.length; i++) {
diffs.push(arr[i] - arr[i - 1]);
}
let max = 0;
// if the difference between two numbers is 0, they're the same.
// the base max variance encountered is 1, otherwise it's 2.
// the boolean zen here is that diffs[0] is falsy when it's 0, and truthy otherwise
let count = diffs[0] ? 2 : 1;
// go through the array of differences,
// and count how many in a row are alternating above/below zero.
for (i = 1; i < diffs.length; i++) {
if ((diffs[i] < 0 !== diffs[i - 1] < 0) && diffs[i] && diffs[i - 1]) {
count++;
} else {
max = Math.max(count, max);
// see above
count = diffs[i] ? 2 : 1;
}
}
// account for the maximum variance happening at the end
return Math.max(count, max);
}
You are overcomplicating things a bit, just increase a counter as long as an element equals the next one, reset on equal:
const counts = [];
let count = 0;
for(let i = 0; i < numbers.length - 1; i++) {
if(numbers[i] === numbers[i + 1]) {
counts.push(count);
count = 0;
} else {
count++;
}
}
counts.push(count);
return counts.sort()[counts.length - 1];

Percentage chance of selected numbers JavaScript

I am creating a spinning wheel of luck. So I have numbers 1 to 6.
Here is some code:
First I get a random number between 0 and 7, so (1,2,3,4,5,6)
var random = Math.floor(Math.random() * 6) + 1;
What I want to do is to set percentage like this:
var goldChance = 10;
var blueChance = 40;
var grayChance = 50;
goldChance are numbers 2 and 5,
blueChance are numbers 6 and 3, and
graychance are numbers 4 and 1.
So when the wheel starts to spin it should show numbers 4 and 1 for 50% of the spin, numbers 6 and 3 for 40% and only rare numbers 2 and 5 should show 10% of the time. That way if you are lucky you will get that gold numbers.
Is there any way to set Math.random() to includes chances?
Thanks in advance!
Don't get a random number 1 <= n <= 6, instead get a random number 0 <= n <= 19 and pick from an array of twenty entries with your distribution of numbers in it:
const numbers = [2, 5, 6, 3, 6, 3, 6, 3, 6, 3, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1];
const result = numbers[Math.floor(Math.random() * numbers.length)];
Example:
const numbers = [2, 5, 6, 3, 6, 3, 6, 3, 6, 3, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1];
const tbody = document.getElementById("results");
const rows = [1, 2, 3, 4, 5, 6].map(number => {
const row = document.createElement("tr");
row.insertAdjacentHTML("beforeend",
`<td>${number}</td><td>0</td><td>0%</td>`);
tbody.appendChild(row);
return row;
});
let draws = 0;
let timer = 0;
doDraw();
function doDraw() {
const number = numbers[Math.floor(Math.random() * numbers.length)];
++draws;
const row = rows[number - 1];
const countCell = row.cells[1];
countCell.textContent = Number(countCell.textContent) + 1;
rows.forEach(row => {
row.cells[2].textContent = (Number(row.cells[1].textContent) / draws * 100).toFixed(2) + "%";
});
timer = setTimeout(doDraw, 1);
}
document.getElementById("stop").addEventListener("click", function() {
clearTimeout(timer);
timer = 0;
});
body {
font-family: sans-serif;
}
td {
text-align: right;
}
The percentages approach the gold (5% each for 2 and 5 [total of 10%]), blue (20% each for 3 and 6 [total of 40%]), and grey (25% each for 1 and 4 [total of 50%]) levels over time as the randomness works itself out...
<table>
<thead>
<tr>
<th>Number</th>
<th>Occurrences</th>
<th>%</th>
</tr>
</thead>
<tbody id="results"></tbody>
</table>
<input id="stop" value="Stop" type="button">
You could use an array with probabilities and check and count against a random value.
This function sets first the return value to the last possible index and iterates until the rest of the random value is smaller than the actual probability.
The probabilities have to sum to one.
function getRandomIndexByProbability(probabilities) {
var r = Math.random(),
index = probabilities.length - 1;
probabilities.some(function (probability, i) {
if (r < probability) {
index = i;
return true;
}
r -= probability;
});
return index;
}
var i,
probabilities = [0.25, 0.05, 0.2, 0.25, 0.05, 0.2],
count = {},
index;
probabilities.forEach(function (_, i) { count[i + 1] = 0; });
for (i = 0; i < 1e6; i++) {
index = getRandomIndexByProbability(probabilities);
count[index + 1]++;
}
console.log(count);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I answered a similar problem for a coding challenge by generating a number 1 <= n <= 100, and then assigning portions of that range to represent the probability desired.
So, for example, to get 1 ~50% of the time, 2 10% of the time, etc.:
if (n > 0 && n <= 50) {
return 1;
} else if (n > 50 && n <= 60) {
return 2;
} else if ...

Is there a JS function that acts like IntStream in Java? [duplicate]

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.
Use this. It creates an array with given amount of values (undefined), in the following example there are 100 indexes, but it is not relevant as here you need only the keys. It uses in the array, 100 + 1, because the arrays are always 0 index based. So if it's given 100 values to generate, the index starts from 0; hence the last value is always 99 not 100.
range(2, 100);
function range(start, end) {
console.log([...Array(end + 1).keys()].filter(value => end >= value && start <= value ));
}

Put the 0 at the same position on two arrays in javascript

Sorry for my english, really really tired...
Imagine two arrays:
var a = [-10, 5, 0, 5, 10, 15];
var b = [-20, 0, 20, 40, 60, 80];
Every value of each array increments by the same number (a by 5 and b by 20).
I would like to find a way to put the zero on the same position on both arrays.
Keep in mind that:
each serie has the same number of elements
you can change the min and max value of each array (the original min and max of a serie have to be a part of the serie, check the example below)
each value of the array increments by the same number, you can change this value
The expected result could be something like
var a = [-10, 5, **0**, 5, 10, 15];
var b = [-60, -30, **0**, 30, 60, 90];
b increments now by 30 and the original min (-20) and max (8) values are included in the interval.
Any idea on how to do that using javascript?
Why I'd like to do that? To solve something like that:
http://peltiertech.com/Excel/Charts/AlignXon2Ys.html
Thanks in advance
Rob
The following is a result of code iteration based on comments. Previous code has been removed for clarity, but remains available in edit history.
This one fixes the zero in the middle of a series, then adjusts values according to the initial requirements. Also rounding to the nearest 5 (previous code was inadequate in that regard, sorry). HTH.
function develop(data) {
if (data.length < 3) {
return data;
}
var lower = data[0];
var upper = data[data.length - 1];
var index = (data.length - 1) / 2;
var numLeft = Math.floor(index);
var numRight = Math.ceil(index);
var leftStep = findStep(lower, numLeft, false);
var rightStep = findStep(upper, numRight, true);
var step = roundStep(Math.max(leftStep, rightStep), 5);
var result = [];
for (var ii = 0; ii < data.length; ii++) {
result[ii] = step * (ii - numLeft);
}
return result;
// ---
function findStep(boundary, numEntries, positive) {
if (positive && boundary <= 0 || !positive && boundary >= 0) {
return 1;
}
return Math.abs(Math.ceil(boundary / numEntries));
}
function roundStep(step, roundTo) {
if (step < roundTo) {
return step;
}
return Math.ceil(step / roundTo) * roundTo;
}
}
function test() {
var testData = [
[-10, -5, 0, 5, 10, 15],
[-20, 0, 20, 40, 60, 80],
[0, 72, 144, 216, 288, 360],
[-30, -25, -20, -15, -10, 0]
];
var results = [];
for (var ii = 0; ii < testData.length; ii++) {
var data = testData[ii];
results.push(JSON.stringify(data) + " => " + develop(data));
}
document.getElementById("results").innerHTML = results.join("<br>");
}
<input type="button" value="test()" onclick="test()" />
<div id="results"></div>
This seems to work, but I'm probably doing couple thins not necessary
var setIndex = function (arr1, arr2) {
var arr1Min = arr1[0];
var arr2Min = arr2[0];
var arr1Max = arr1[arr1.length-1];
var arr2Max = arr2[arr2.length-1];
var length = arr1.length;
var newRatio;
var newMin;
var newMax;
var ratioArr1 = arr1Max/arr1Min;
var ratioArr2 = arr2Max/arr2Min;
if(ratioArr1 < ratioArr2){
newMin = calcNewMin(arr1Min, arr1Max, ratioArr2);
newMax = ratioArr2 * newMin;
newRatio = (newMax - newMin)/(length-1);
arr1 = [];
for(var i = 0; i < length; i++){
arr1.push(newMin + (i * newRatio));
}
return [arr1, arr2];
} else {
newMin = calcNewMin(arr2Min, arr2Max, ratioArr1);
newMax = ratioArr1 * newMin;
newRatio = (newMax - newMin)/(length-1);
arr2 = [];
for(var i = 0; i < length; i++){
arr2.push(newMin + (i * newRatio));
}
return [arr1, arr2];
}
};
var calcNewMin = function(min, max, ratio){
var count = 1;
var newMin = min;
var newMax = max;
while(newMax <= max){
count++;
newMin = min - count;
newMax = newMin * ratio;
}
return newMin;
};
Compare the array ranges first, if one array includes the range of the other one then the answer is a series with an increment higher than the array that increments where the start of the range is:
0 - increment * (position of 0)
In your example:
var a = [-10, 5, 0, 5, 10, 15];
var b = [-20, 0, 20, 40, 60, 80];
b includes the range of a, so any series that have an increment higher than b and obeys the rules at the beginning are valid solutions to the problem:
[-20, 0, 20, 40, 60, 80]
[-21, 0, 21, 42, 63, 84]
[-22, 0, 22, 44, 66, 88]
...
[-60, 0, 60, 120, 180, 240]
All of these series include the range of a.
It gets a little bit trickier when the ranges overlap:
[-10, 0, 10, 20, 30]
[ 0, 20, 40, 60, 80]
The idea behind is the same. We'll pick the series with the smallest value:
[-10, 0, 10, 20, 30]
From this, we'll need to find a higher increment so that it satisfies:
start + (inc * length) > max of other series.
Where start:
0 - (inc * pos of 0 in picked series)
Moving stuff around you get:
inc > (max value / (length - pos of 0 in picked series))
So in this example:
inc > 80 / (5 - 2)
inc > 80 / 3
inc > 26.666
Lets try it with an increment of 27 and a start of -27:
[-27, 0, 27, 54, 81]
Now, that you we know how to solve the problem, lets try it with code:
function getMinMax(a, b){
var last = a.length - 1,
min = a[0] < b[0] ? a : b,
max = a[last] > b[last] ? a : b;
return { min : min, max : max };
}
function closestInc(range){
if(range.min === range.max){
return range.min[1] - range.min[0];
} else {
var last = range.min.length - 1,
maxValue = range.max[last],
posOfCero = range.min.indexOf(0);
return (maxValue/(range.min.length - posOfCero));
}
}
So, all the possible answers would be any series with an increment value bigger than closestInc(a, b) and a start of -closestInc(a,b) * posOfCero.
Here's a function that prints out all possible values slowly:
function createSeries(inc, posOfCero, count) {
var series = [],
start = -(inc * posOfCero);
for (var i = 0; i < count; i++) {
series.push(start + (inc * i));
}
return series;
}
var a = [-10, 5, 0, 5, 10, 15],
b = [-20, 0, 20, 40, 60, 80],
ranges = getMinMax(a, b),
inc = closestInc(ranges),
posOfCero = ranges.min.indexOf(0);
setTimeout(function printSeries(i) {
console.log(createSeries(inc + 1, posOfCero, range.min.length));
setTimeout(printSeries, 1000, i + 1);
}, 1000, 1);
A snippet below:
function getMinMax(a, b) {
var last = a.length - 1,
min = a[0] < b[0] ? a : b,
max = a[last] > b[last] ? a : b;
return {
min: min,
max: max
};
}
function closestInc(range) {
if (range.min === range.max) {
return range.min[1] - range.min[0];
} else {
var last = range.min.length - 1,
maxValue = range.max[last],
posOfCero = range.min.indexOf(0) + 1;
return (maxValue / (range.min.length - posOfCero));
}
}
function createSeries(inc, posOfCero, count) {
var series = [],
start = -(inc * posOfCero);
for (var i = 0; i < count; i++) {
series.push(start + (inc * i));
}
return series;
}
//var a = [-10, 5, 0, 5, 10, 15],
// b = [-20, 0, 20, 40, 60, 80],
var a = [-10, 0, 10, 20, 30],
b = [ 0, 20, 40, 60, 80],
ranges = getMinMax(a, b),
inc = closestInc(ranges),
posOfCero = ranges.min.indexOf(0);
setTimeout(function printSeries(i) {
console.log(createSeries(Math.round(inc + i), posOfCero, ranges.min.length));
setTimeout(printSeries, 1000, i + 1);
}, 1000, 1);
A last note, this aren't all possible series to match your rules (there might still be some valid increments between the series).
You can use Array.push and Array.unshift like this jsFiddle

Use javascript to sort an array of ints by the distance the element is from a given target

Algorithm: distanceSort(array, target)
Input: An array of ints sorted from least to greatest and an int to measure distance from
Output: The array sorted by distance from target
Example
distanceSort([-10,-6,3,5], 1)
returns [3, 5, -6, -10]
Here's a way to do it. Using Array.sort function
var a = [-10, -6, 3, 5, 99, 76, -100];
function distanceSort(arr, target) {
return arr.sort(function(a, b) {
var distance1 = Math.abs(target - a);
var distance2 = Math.abs(target - b);
return distance1 == distance2 ? 0 : (distance1 > distance2 ? 1 : -1);
});
}
console.log(distanceSort(a, 100)); //[99,76,5,3,-6, -10, -100]
console.log(distance(a, -5)); //[-6, -10, 3, 5, 76, -100, 99]
First, perform a binary search (I always copy from here) to find the target inside the array (if it exists, or the immediatelly greater otherwise).
Then, keep two pointers moving in oposing directions adding always the element with less distance from the target.
The binary search is O(log n) and moving the pointers is O(n). The overall algorithm is O(n).
function lowerBound(arr, target) {
var first = 0,
count = arr.length;
while (count > 0) {
var step = count / 2;
var it = first + step;
if (arr[it] < target) {
first = it + 1;
count -= step + 1;
} else {
count = step;
}
}
return first;
}
function distanceSort(arr, target) {
var answer = [];
var j = lowerBound(arr, target);
var i = j-1;
while (i >= 0 || j<arr.length) {
if (j >= arr.length || target-arr[i]<arr[j]-target)
answer.push(arr[i--]);
else
answer.push(arr[j++]);
}
return answer;
}
console.log(distanceSort([-10,-6,3,5], 1)); //[3, 5, -6, -10]
console.log(distanceSort([-10,-6,3,5], -11)); //[-10, -6, 3, 5]
console.log(distanceSort([-10,-6,3,5], -10)); //[-10, -6, 3, 5]
console.log(distanceSort([-10,-6,3,5], 5)); //[5, 3, -6, -10]
console.log(distanceSort([-10,-6,3,5], 6)); //[5, 3, -6, -10]
var distanceSort = function distanceSort(nArr, x){
return nArr.sort(function(n1, n2){
return Math.abs(n1 - x) - Math.abs(n2 - x);
});
}
console.log(distanceSort([-10,-6, 57, 54, 11, -34, 203, -140, 3, 5], 1));
//--> [3, 5, -6, 11, -10, -34, 54, 57, -140, 203]
It translates nicely into ECMA6 (if you're using Babel) :
var distanceSort = (nArr, x) =>
(nArr.sort((n1, n2) =>
(Math.abs(n1 - x) - Math.abs(n2 - x))));

Categories