I want to create a method that takes a list of numbers and can reduce the array by a supplied variance.
Variance Explained:
The variance should match a value below and above. So for example, the variance of 100 equated against the value: 5460 should match any value between 5410 - 5510. (50 below the equated value and 50 above)
For instance if I have the array:
[ 1576420754, 1576420756, 1576593554, 1581172759, 1581172764 ]
I want to created a method filterSimilarValues(array, variance = 100)
Which would give me the following output:
[ 1576420756, 1576593554, 1581172764 ]
I have tried a couple things, like
const filterSimalarValues = (array, variance = 100) => {
let filteredArray = [];
for (let i = 0; i < array.length; i++) {
const number = array[i];
if (number >= number - (variance / 2) && number <= number + (variance / 2)) {
return;
}
filteredArray.push(number);
}
return filteredArray;
};
Use Array.filter() to retain only numbers that the absolute difference (Math.abs()) between the them and the next number is greater or equal variance / 2. The last item (i === array.length - 1) is included by default.
const filterSimalarValues = (array, variance = 100) =>
array.filter((n, i) =>
i === array.length - 1 ||
Math.abs(n - array[i + 1]) >= variance / 2
)
const data = [1576420754, 1576420756, 1576593554, 1581172759, 1581172764]
const result = filterSimalarValues(data)
console.log(result)
I would try something like:
const filterSimalarValues = (array, variance = 100) =>
array.reduce((acc, curr) => {
const variant = acc.find(
item => curr > item - variance / 2 && curr < item + variance / 2,
);
if (!variant) {
acc.push(curr);
} else {
acc[acc.indexOf(variant)] = curr;
}
return acc;
}, []);
Also, in my opinion you should be more specific with your question, there are a lot of possible (edge) cases not covered.
As per the explanation provided and my understanding of variance, you want to limit the numbers between the variance range. I am assuming you have a minimum of 2 elements but you can add checks for that case.
const filterSimalarValues = (array, variance = 100) => {
const filteredArray = [];
filteredArray.push(array[0]);//bench mark to check
for (let i = 1; i < array.length; i++) {
const number_lower_bound = array[i] - variance/2;
const number_upper_bound = array[i] + variance/2;
var bound_exist = false;
for(var bound = number_lower_bound;bound<=number_upper_bound;bound++){
if (filteredArray.includes(bound)) {
bound_exist = true;
break;
}
}
if(!bound_exist){
filteredArray.push(array[i])
}
}
}
return filteredArray;
};
This is what I have come up with, it works, but there might be a better solution so I am not accepting my own answer.
const filterSimilarValues = (array, variance = 100) => {
let filteredArray = [];
array.forEach(number => {
const start = number - (variance / 2);
const end = number + (variance / 2);
const range = Array(end - start + 1).fill().map((_, idx) => start + idx);
const found = range.some(r => filteredArray.includes(r))
if (!found) {
filteredArray.push(number);
}
});
return filteredArray;
};
Related
I've been thinking that I found solution, but tests fail (I have no access to tests).
The task:
We want to organize a chess tournament.
One day - one game.
And as entry point we have number of days - that we can afford.
Tournament rules:
We have number of teams: if number is even - we divide number of teams by 2 and that's the number of games - so they fight 1 vs 1 in pairs, who lost is excluded from the competition.
For example : 20 teams => 10 games => 10 teams left.
And if the even number of teams left - we make the same iteration: divide by 2 into pairs.
10 teams - 5 games - 5 teams left.
And now we have Odd number!
If we have odd number from start or get it in process - number of games is counted by another rule:
everyone needs to play with everyone. So the formula is = n * (n-1) / 2.
For example 5 teams = 10 games.
Or any odd number (to sum it all - if we have Odd number we always ignore first rule with pairs).
So:
We have number of days and need to find all possible team numbers for this days, it can be more than one value, for example : 3 days, number of teams that is correct is 3 and 4;
Entry point 3, function logs 3 and 4;
If that's impossible to find teams - return -1;
1 ≤ 𝑁 ≤ 1000000
Here is my solution - It ouputs correct values, but tests fail
Can someone help to find what I did wrong?
(Sorry for code mess, I had different thoughts every time)
function solve(input) {
let quadraticEquation = (number) => {
let a = 1;
let b = -1;
let c = -number*2
if(a == 0)
return 'false';
let res = {};
let D = b * b - 4 * a * c;
let tmp = [];
if(D < 0)
return 'false';
res['discriminant'] = D;
if(D == 0)
res["quadratic roots"] = (-b + Math.sqrt(D)) / (2 * a);
else if(D > 0){
tmp.push((-b + Math.sqrt(D)) / (2 * a));
tmp.push((-b - Math.sqrt(D)) / (2 * a));
res["quadratic roots"] = tmp;
}
return tmp;
}
let start = input;
let xValues = quadraticEquation(start).map(item => Math.abs(item)).filter(item => item % 2 !== 0);
let arrayToUse = []
for(i = 0; i <= xValues[xValues.length-1]; i++) {
if( i === 1) {
continue;
}
if (!(i%2)) {
continue
}
arrayToUse.push(i);
}
const answer = [];
arrayToUse.forEach(item => {
let startValue = item;
let fightsNumber = item * (item - 1) / 2;
let loop = 0;
if (fightsNumber === start) {
answer.push([startValue, loop])
} else {
do {
loop++;
fightsNumber += startValue;
if (fightsNumber === start) {
answer.push([item, loop] )
}
startValue *= 2;
} while (fightsNumber < start)
}
})
function getTeams (answer) {
const finalResult = [];
answer.forEach(item => {
if(item[1] === 0) {
finalResult.push(item[0]);
} else {
let initialValue = item[0];
for(i=0; i < item[1]; i++) {
initialValue += item[0];
item[0] *= 2;
}
finalResult.push(initialValue);
}
})
let initialValue = 2;
let fightCounter = 0;
for (i = 0; i <= start; i++) {
fightCounter += initialValue / 2
if (fightCounter === start) {
finalResult.push(initialValue);
}
initialValue *= 2;
}
return finalResult;
}
let finalString = ''
const arrayToLog = getTeams(answer).sort((a,b) => a - b);
if(arrayToLog.length !== 0) {
arrayToLog.forEach(item => {
finalString += item + '\n';
})
} else {
finalString += -1;
}
return finalString;
}
console.log(solve(325))
console.log(solve(3))
console.log(solve(15))
console.log(solve(21))
console.log(solve(10))
console.log(solve(1))
console.log(solve(5))
console.log(solve(9))
Note: for those who didn't see it, this is a followup to the earlier question Backward recurssion.
That's a lot of code for a fairly simple problem. I don't have the inclination or the time right now to go through it carefully.
So here is how I might solve this problem, using brute force to look for all the resulting number of days for any number of players up to days + 1, and then filtering out what we want from that list:
const range = (lo, hi) =>
[... Array (hi - lo + 1)] .map ((_, i) => i + lo)
const qwe = (n) =>
n % 2 == 1
? n * (n - 1) / 2
: n / 2 + qwe (n / 2)
const solve = (days) => {
const daysByPlayers = range (1, days + 1) .map (qwe)
const res = daysByPlayers .reduce ((a, d, i) => d == days ? [...a, i + 1] : a, [])
return res .length == 0 ? [-1] : res
}
const tests = [325, 3, 15, 21, 10, 1, 5, 9]
tests .forEach (n => console .log (`${n}: ${JSON.stringify(solve(n))}`))
.as-console-wrapper {max-height: 100% !important; top: 0}
This matches your output as far as I can tell. But I don't know what tests you say are failing, so perhaps there's something wrong with this as well.
There is extra complexity in here because of your desire for a -1 output in the missing cases. If an empty array would serve, then this is cleaner:
const solve = (days) =>
rng (1, days + 1) .map (qwe)
.reduce ((a, d, i) => d == days ? [...a, i + 1] : a, [])
And there are many variants. This one returns the maximum number of players that can be accommodated in some number of days up to the given value:
const solve = (days) => {
const daysByPlayers = rng (1, days + 1) .map (qwe)
const max = Math.max (... daysByPlayers .filter (d => d <= days))
return days - daysByPlayers .reverse () .indexOf (max) + 1
}
And this one gives you all the possible number of players available to achieve that maximum:
const solve = (days) => {
const daysByPlayers = rng (1, days + 1) .map (qwe)
const max = Math.max (... daysByPlayers .filter (d => d <= days))
return daysByPlayers .reduce ((a, d, i) => d == max ? [...a, i + 1] : a, [])
}
(For instance, solve (20) would return [10, 16], which are the possible number of players to handle in exactly 15 days, the largest value found no larger than 20.)
And finally, we could list all the numbers of players we could accommodate in up to a certain number of days:
const solve = (days) => {
const daysByPlayers = rng (1, days + 1) .map (qwe)
const max = Math.max (... daysByPlayers .filter (d => d <= days))
return daysByPlayers .reduce ((a, d, i) => d <= max ? [...a, i + 1] : a, [])
}
For instance, solve (20) would return [1, 2, 3, 4, 5, 6, 8, 10, 12, 16], which are the only numbers of players that could be handled in no more than 20 days.
When I brought up the quadratic formula in the earlier question it was just to note that in certain instances, we can find an exact solution, solving for d in n * (n - 1) / 2) = d. That meant that if 8 * d + 1 is a perfect square, then we can choose n to be (1 + sqrt (8 * d + 1)) / 2 to find one solution. But that's not of any real help in the general case.
So I have a dataset of 16 items, I want to loop through them every 5 items after the first set of 6 so the columns are 6 5 5.
Initially I tried something like this, but then I remembered I had that one orphaned item.
if(thisI <= 6) {
y = prevtitle.position[0];
} elseif(thisI % 5 == 0) {
y = prevtitle.position[0] + w + (p *3);
} else {
y = prevtitle.position[0];
}
Not sure if there is a simple way to do the first 6 and then the next ones in five without a bunch of nested if statements.
Using Array#splice:
const splitArr = (arr = []) => {
const array = [...arr];
const res = array.length ? [array.splice(0, 6)] : [];
while(array.length) res.push(array.splice(0, 5));
return res;
}
console.log( splitArr([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]) );
Would a simple ternary expression work for you?
let num = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]
num.forEach((n,i) => {
y = (i<=6 || i % 5 == 0) ? prevtitle.position[0] : prevtitle.position[0] + w + (p *3) ;
})
I presume that you're skipping the first position, and jumps directly to the 6th position.
Just use a normal for loop.
Calculate the remainder for the number of steps that you will make. 16 % 5 results in 1 remainder.
step - countForZero + remainder sets the start point in the for loop.
i += step replaces the typical i++ in the for loop.
The method below can make any kind of leap, and it doesn't matter how many items there are in the array.
let num = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16];
function getEvery(step, arr) {
let newArr = [],
remainder = arr.length % step,
countForZero = 1;
for (let i = step - countForZero + remainder; i < arr.length; i += step) {
newArr.push(arr[i]);
}
return newArr;
}
console.log( getEvery(5, num) );
I can't wrap my head around a piece of code using loops and functions in javascript.
I have a function which generates random numbers (between a min and max), see below:
const getRandomNumber = (min, max) => {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
Below a simple Function which returns true if 2 random numbers add up to 10, else it returns false:
const Function1 = (n1, n2) => (n1 + n2 === 10) ? true : false
Below i will use Function1 to return n1 and n2:
const Function1Check= () => {
const n1 = getRandomNumber(-10, 10);
const n2 = getRandomNumber(-10, 10);
if(Function1(n1, n2)) {
return [n1, n2]
} else {
return {false}
}
}
const LoopFunction = () => {
while(Function1Check === false) {
Function1Check();
if(Function1Check) {break;}
}
}
My while loop does not work correctly, what am i missing?
Hope you guys can help me out and point me in the right direction using vanilla javascript.
Thanks in advance.
Greetings.
You can use a do.. while loop and quit after a maximum number of attempts to avoid an infinite loop.
The loop will also terminate once the target length of the array is reached:
const getRandomNumber = (min, max) => {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min; // max & min both included
}
const Function1 = (n1, n2) => (n1 + n2 === 10) ? true : false
const Function1Check = () => {
const n1 = getRandomNumber(-10, 10);
const n2 = getRandomNumber(-10, 10);
if(Function1(n1, n2)) {
return [n1, n2]
} else {
return false;
}
}
let rightNumbers = [];
let attempts = 0;
let maxAttempts = 1000;
let target = 5;
do {
let numbers = Function1Check();
if (numbers) {
rightNumbers.push(numbers);
}
} while (rightNumbers.length < target && (attempts++) < maxAttempts)
console.log(`Found ${target} number(s) after ${attempts} attempts`);
console.log(`Numbers:`, rightNumbers)
If your program freezes that's a clear sign that your while loop doesn't stop.
I'd approach it the following way:
const AMOUNT_OF_RESULTS = 5;
const results = [];
// This would be the correct predicate to stop your while loop.
// You want to stop looping once you have 5 correct results.
while (results.length < AMOUNT_OF_RESULTS ) {
const result = checkRightNumbers();
// If result is not false(an array in your case)
if(!result) {
// Then add the result to the array of results:
results.push(result);
}
}
The loop will continue generating results until it filled the quota(AMOUNT_OF_RESULTS).
A method which achieves what you are trying to do looks something like this:
const getSolutionSet = () => {
let answers = [];
while(answers.length < 5){
let check = checkRightNumbers();
if(check){
answers.push(check);
}
}
return answers;
}
You could make it a bit more advanced, by passing the amount of results as a parameter
const getSolutionSet = (numResults) => {
let answers = [];
while(answers.length < numResults){
let check = checkRightNumbers();
if(check){
answers.push(check);
}
}
return answers;
}
You should keep in mind, that the probability is pretty low to find an exact match, so adding a max number of tries would also be a good idea
Question:
Here is my solution:
function smallestDifference(arrayOne, arrayTwo) {
const combinedArray = [...arrayOne, ...arrayTwo];
combinedArray.sort((a, b) => a - b);
let smallestDifference = Infinity;
let arrayOneInt = null;
let arrayTwoInt = null;
for (let i = 0; i < combinedArray.length - 1; i++) {
if (Math.abs(combinedArray[i] - combinedArray[i+1]) < smallestDifference) {
if (arrayOne.includes(combinedArray[i]) && arrayTwo.includes(combinedArray[i+1])) {
smallestDifference = Math.abs(combinedArray[i] - combinedArray[i+1]);
arrayOneInt = combinedArray[i];
arrayTwoInt = combinedArray[i+1];
} else if (arrayOne.includes(combinedArray[i+1]) && arrayTwo.includes(combinedArray[i])) {
smallestDifference = Math.abs(combinedArray[i] - combinedArray[i+1]);
arrayOneInt = combinedArray[i+1];
arrayTwoInt = combinedArray[i];
}
}
}
return [arrayOneInt, arrayTwoInt];
}
Here is the given optimal solution
function smallestDifference(arrayOne, arrayTwo) {
arrayOne.sort((a, b) => a - b);
arrayTwo.sort((a, b) => a - b);
let idxOne = 0;
let idxTwo = 0;
let smallest = Infinity;
let current = Infinity;
let smallestPair = [];
while (idxOne < arrayOne.length && idxTwo < arrayTwo.length) {
let firstNum = arrayOne[idxOne];
let secondNum = arrayTwo[idxTwo];
if (firstNum < secondNum) {
current = secondNum - firstNum;
idxOne++;
} else if (secondNum < firstNum) {
current = firstNum - secondNum;
idxTwo++;
} else {
return [firstNum, secondNum]
}
if (smallest > current) {
smallest = current;
smallestPair = [firstNum, secondNum];
}
}
return smallestPair;
}
For the above given optimal solution, it says the time complexity is O(nLog(n) + mLog(m)) and space complexity is O(1). Does my solution above it also match this time complexity?
You have loop over combinedArray which has lenght N + M. Within this loop you have arrayOne.includes and arrayTwo.includes with O(N) and O(M) time complexities.
So you have at least O((N + M) ^ 2) which is bigger than O(nLog(m) + mLog(n))
I have a list of positive integers e.g. 15, 29, 110, and a target e.g. 44. I'm trying to find all possible combinations which sum to the target but importantly, the numbers in the set can be used multiple times e.g.
Target = 44
Result = 1x15, 1x29
Target = 307
Result = 2x110, 3x29
I found a dynamic programming solution which works when the combination is no more than one of each number. So Target 44 works but not my 307 example (returns Not Found).
How can the multiples or number reuse be done?
function subset(people, min, max)
{
var subsets = [];
subsets[0] = '';
for (var person in people)
{
for (var s = min-1; s >= 0; --s)
{
if (s in subsets)
{
var sum = s + people[person];
if (!(sum in subsets))
{
subsets[sum] = subsets[s] + ' ' + person;
if (sum >= min && sum <= max)
{
return subsets[sum];
}
}
}
}
}
return 'Not found';
}
var p = {
optionA:15,
optionB:29,
optionC:110
};
var qty = 307;
console.log(subset(p, qty, qty));
Try this recursive solution:
function subset(people, min, max) {
const pairs = Object.entries(people),
results = [],
getSum = multiplications => multiplications.reduce((sum, multiplicator, position) =>
sum + pairs[position][1] * multiplicator, 0),
formatResult = result => result.map(multiplications =>
multiplications.reduce((res, multiplicator, position) =>
(multiplicator > 0 ? res.push(`${multiplicator}x${pairs[position][1]}`) :
res, res), []));
function findSums(multiplications, position) {
let s;
while((s = getSum(multiplications)) <= max) {
if (s >= min) {
results.push([...multiplications]);
}
if (position < pairs.length - 1) {
const m = [...multiplications],
nextPosition = position + 1;
m[nextPosition]++;
findSums(m, nextPosition);
}
multiplications[position]++;
}
}
findSums(pairs.map(_ => 0), 0);
return results.length > 0 ? formatResult(results) : "Not found";
}
var p = {
optionA:15,
optionB:29,
optionC:110
};
var qty = 307;
console.log(subset(p, qty, qty));
Change the second loop in such way:
for (var s = 0; s <= wantedSum - people[person] ; s++)
Using this approach you fill all entries of subsets[] array\list where index is multiple of people[person] (instead of single entry). For example, with value 3 you fill 3,6,9,12... entries.