Break out of map - javascript

so i have this problem where if the value in the array is higher than entered value it should do something and then stop the loop and don't touch the remaining values in the array.. Here's the code so far:
const percentages = [];
let enteredValue = parseInt(event.target.value, 10);
range.map((rangeValue, i) => {
if (rangeValue <= enteredValue) {
percentages.push(100);
enteredValue = enteredValue - rangeValue;
} else {
percentages.push(enteredValue * 100 / (rangeValue));
return;
}
});

Using .some you can get iteration functionally similar to .forEach, map or for loop but with the ability to break through return instead.
range.some(function(rangeValue , i) {
if (rangeValue <= enteredValue) {
percentages.push(100);
enteredValue = enteredValue - rangeValue;
return true
}
percentages.push(enteredValue * 100 / (rangeValue));
});
Read more about .some in es6 here

Just use a good old for loop:
const percentages = [];
let enteredValue = parseInt(event.target.value, 10);
for(const range of ranges) {
percentages.push(Math.min(100, (enteredValue / range) * 100));
enteredValue -= range;
if(enteredValue <= 0) break;
}

The other answers are perfectly sufficient. However, I would like to add that the map function isn't best used for performing iterations. What the map function does is perform the as argument passed in callback on every element of the array. It then returns the new array. So map is more usefull when you are in need of a mutated copy of the Array (e.g. all the elements multiplied by 10).
For your purposes other logic like a regular for loop will be a more elegant solution.

You can use reduce instead of map. The reduce senses a change in the original array.
let enteredValue = parseInt(event.target.value, 10);
const percentages = range
.slice(0) // if you don't want to mess up the original array
.reduce((percentages, rangeValue, _, rangeCopyArray) => {
if (rangeValue <= enteredValue) {
percentages.push(100);
enteredValue -= rangeValue;
} else {
percentages.push(enteredValue * 100 / rangeValue);
rangeCopyArray.length = 0; // the loop ends here
}
return percentages;
}, [])
or shorter but less clear
const percentages = [...range].reduce(({p, eV}, rV, _, arr) => ({
eV: eV-rV,
p: [...p, rV <= eV ? 100 : eV*100/rV + (arr.length = 0)]
}), {p: [], eV: parseInt(event.target.value, 10)}).p;

Related

All possible variations of terms to add up to sum in a given amount of piles?

How to get all possible variations of terms to add up to sum, in a given amount of piles, using javascript?
Let's say I have a sum of 10 and I want to split this into 4 piles with positive terms and zeros only.
function getCombinations(sum, piles){
...
}
getCombinations(10,4);
Returns something like this in a two dimensional array:
[
[3,3,3,1],
[3,3,1,3],
[7,1,1,1],
[10,0,0,0],
...
]
It's not mandatory to return [3,3,3,1] and [3,3,1,3] as different solutions, fastest way will do. I will only work with small numbers, max sum will probably be 10.
It's a variation of the Count the coins problem, http://rosettacode.org/wiki/Count_the_coins, but I want the solutions returned, I have a given set of piles and I use all positive terms (and zero) not only specific coin values.
this should do the trick:
const matrix = (num, cols) => {
const matrix = [[num, ...[...Array(cols-1)].map(() => 0)]];
const hashes = new Set;
const coef = Math.pow(10, cols-1);
let digits = 10 * coef - 1;
while (digits-- >= coef) {
const nums = ('' + digits).split('').map(d => +d);
const hash = nums.sort((a, b) => b - a).join('');
if (hashes.has(hash) || nums.reduce((a, b) => a + b, 0) !== num)
continue;
hashes.add(hash);
matrix.push(nums);
}
return matrix;
};
console.log(matrix(10, 4));
Here is a pratical way of doing this:
function getCombinations(sum, piles){
var array =[];
for(var i = 1; i <= piles; i ++) {
var subArray = Array.apply(null, Array(4)).map(
function () {
return Math.floor(Math.random() * sum)});
array.push(subArray);
}
return array;
}
var result = getCombinations(10,4);

Simple for loop does not seem work properly

I'm having a trouble to get an array with numbers from 1 to 16 randomly without repetition.
I made num array to put numbers from createNum function.
The createNum function has a for loop which gets numbers from 1 to 16 until if statement push 16 numbers into num array.
At the end, I run createNum() and display numbers on the web.
While I'm making this code it sometimes worked but now it's not working.
can somebody point out where I made mistakes?
let num = [];
function createNum () {
for (i = 0; i <= 15;) {
let numGen = Math.floor(Math.random() * 15) + 1;
if (!num.includes(numGen)) {
num.push(numGen);
i++;
};
};
}
console.log(createNum());
document.getElementById("selectedNumbersShownHere").innerHTML = num;
console.log(num);
This is because Math.random() never returns 1, so at the end Math.floor(Math.random() * 15) will returns maximum 14 and adding it to 1 will get you maximum 15.
Use Math.ceil instead of Math.floor i.e
let num = [];
function createNum () {
for (i = 0; i <=15;) {
let numGen = Math.ceil(Math.random() * 16);
console.log(numGen)
if (!num.includes(numGen)) {
num.push(numGen);
i++;
};
};
}
console.log(createNum());
document.getElementById("selectedNumbersShownHere").innerHTML = num;
console.log(num);
Hope it helps!
for (i = 0; i <= 15;) generates 16 numbers, but Math.floor(Math.random() * 15) + 1 only have 15 possible values (1~15).
A shuffle function is recommended. Your function would be slow if you generate a large size shuffled array.
How can I shuffle an array?
Your loop seems never finish because the probability to get the last value is very low, and never can happen in a short time.
Plus :
your formula is wrong : let numGen = Math.floor(Math.random() * 15) + 1;
and should be................: let numGen = Math.floor(Math.random() * 16) + 1; value 16
see => Generating random whole numbers in JavaScript in a specific range?
do this:
function createNum()
{
let num =[], lastOne =136; // 136 = 1 +2 +3 + ... +16
for (;;)
{
let numGen = Math.floor(Math.random() *16) +1;
if (!num.includes(numGen))
{
lastOne -= numGen;
if (num.push(numGen)>14) break;
}
}
num.push(lastOne); // add the missing one (optimizing)
return num;
}
let unOrdered_16_vals = createNum();
/*
document.getElementById("selectedNumbersShownHere").textContent = unOrdered_16_vals.join('');
*/
console.log( JSON.stringify( unOrdered_16_vals ), 'length=', unOrdered_16_vals.length );
console.log( 'in order = ', JSON.stringify( unOrdered_16_vals.sort((a,b)=>a-b) ) );
remark : The push() method adds one or more elements to the end of an array and returns the new length of the array.
The problem in your code is that your looking for 16 distinct numbers out of 15 possible values.
The reason for this is that Math.floor(Math.random() * 15) + 1; will only return values between 1 and 15, but your loop will run until you have 16 unique values, so you enter into an infinite loop.
What you're basically trying to achieve is randomly shuffle an array with values from 1 to 16.
One common solution with good performance (O(n)) is the so-called Fisher-Yates shuffle. Here is code that addresses your requirements based on an implementation by Mike Bostock:
function shuffle(array) {
let m = array.length, t, i;
while (m) {
i = Math.floor(Math.random() * m--);
t = array[m];
array[m] = array[i];
array[i] = t;
}
return array;
}
// create array with values from 1 to 16
const array = Array(16).fill().map((_, i) => i + 1);
// shuffle
const shuffled = shuffle(array);
console.log(shuffled);
Compared to your approach and the approach of other answers to this question, the code above will only make 15 calls to the random number generator, while the others will make anywhere between 16 and an infinite number of calls(1).
(1) In probability theory, this is called the coupon collector's problem. For a value n of 16, on average 54 calls would have to be made to collect all 16 values.
Try like this :
let num = [];
function createNum () {
for (i = 0; num.length <= 17; i++) {
let numGen = Math.floor(Math.random() * 16) + 1;
if (!num.includes(numGen)) {
num.push(numGen);
};
};
}
console.log(createNum());
document.getElementById("selectedNumbersShownHere").innerHTML = num;
console.log(num);
Please find the working demo here
This is an infinite loop error. Because your loop variable "i" is always less than or equal to 15. and your i++ is inside the if statement. You can achieve it in multiple ways. Below is one sample.
let num = [];
function createNum () {
for (i = 0; i <= 15;) {
let numGen = Math.floor(Math.random() * 15) + 1;
if (!num.includes(numGen)) {
num.push(numGen);
};
i++;
};
}
console.log(createNum());
document.getElementById("selectedNumbersShownHere").innerHTML = num;
console.log(num);
sorry, I'm "passionate" about this question, and I can't resist presenting a second answer, which is IMHO: the best!
function createNum ()
{
let num = []
for (let len=0;len<16;)
{
let numGen = Math.ceil(Math.random() * 16)
if (!num.includes(numGen))
{ len = num.push(numGen) }
}
return num
}
let unOrdered = createNum();
console.log( JSON.stringify( unOrdered ) );
/*
document.getElementById("selectedNumbersShownHere").textContent = unOrdered_16_vals.join('');
*/

Sized array of random unique numbers

I was wondering what was the most concise way to get an array of a certain size, of unique random numbers.
I get random numbers like this:
times(4, () => random(30, 95));
However this is not unique. I can filter this with uniq but I need to gurantee length of 4 of array. And I want to do it the lodash way. Any ideas?
Much easiear would be...
const uniqRandomNumbers = _.sampleSize(_.range(30, 95), 4);
console.log(uniqRandomNumbers);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.min.js"></script>
I know this isn't "the lodash way", but it guarantees uniqueness, and allows you to use the same arguments as you were using before. It also scales better than methods that require a binary or linear search through an array, as set.has() is O(1) on average, rather than O(log(n)) or O(n).
function uniqRandom (times, ...args) {
const set = new Set()
while (times > 0) {
const rand = _.random(...args)
if (!set.has(rand)) {
set.add(rand)
times--
}
}
return Array.from(set)
}
console.log(uniqRandom(4, 30, 33));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.min.js"></script>
I solved it using from a functional programming perspective. refillRandom is a recursive function that checks the number of items left to generate and calls itself again until the are the required number of items.
It also throws an Error when is imposible to generate the sequence, if the distance between min and max random number is greater than the required unique items. It's better to throw an Error than waiting forever.
const generator = (min, offset) => () =>
Math.floor(Math.random() * offset + min);
const refillRandom = (list, min, max, times) => {
const offset = max - min,
num = times - list.length;
if (times > offset) {
throw new Error("Imposible to generate it");
}
const result = _.uniq(_.times(num, generator(min,offset)));
if (result.length < num) {
return result.concat(
refillRandom(list, min, max, num - result.length)
);
}
return result;
}
const r = refillRandom([], 30, 95, 4);
console.log(r);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.min.js"></script>
EDIT: I found another solution, I mantain an ordered array of generated numbers and increment the generated number so it get mapped to a number that has not been generated yet. This way I only call random the times specified.
const randomGenerator = (min, offset, generated, times) => {
if (!times || !offset) return generated;
var number = Math.floor(Math.random() * offset + min);
const len = generated.length;
for (var i = 0; i < len; i++) {
if (generated[i] <= number) {
number++;
} else {
generated.splice(i, 0, number);
return randomGenerator(min, offset - 1, generated, times - 1);
}
}
generated[i] = number;
return randomGenerator(min, offset - 1, generated, times - 1);
};
const r = randomGenerator(30, 95 - 30, [], 12);
console.log(r);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.min.js"></script>

Print the value of the first element that occurs N number of times

So I'm making this ReactJS application.
Part of a filtering system I have the following problem:
Simplified...
I have an array, let's say its simple one like let arr = [1,7,4,3,4,7];
and I also input an N variable from the user that is a simple integer input.
I need to return or console.log() the integers from the array that is repeated N times. If there is nonsuch repeating number log err msg or return -1;
For instance,
let arr = [1,7,4,3,4,7]; and let n = 2; i get 7- cos 7 repeats 2 times
let arr = [7,4,5,3,5,5,3,4,3,2,1]; and let n = 3; i get 5 - cos 5 repeats 3 times
let arr = [1,6,4,6,4,6]; and let n = 4; i get -1 or cl("err") - cos nothing repeats 4 times
Code from comments:
const getRepeatingNumber = (arr, n) => {
for (unit of arr) {
if (unit === maj_unit) count++;
else {
count--;
if (count === 0) {
maj_unit = unit;
count = 1;
}
}
}
return maj_unit;
}
You can use array#every, create an accumulator and place the number as key and its frequency as value, once the value reaches the specified number of occurrence, break from the loop using return false, then return the result.
const getRepeatingNumber = (arr, count) => {
let result = -1, hash = {};
arr.every(n => {
hash[n] = (hash[n] || 0) + 1;
if(hash[n] === count) {
result = n;
return false;
}
return true;
});
return result;
}
console.log(getRepeatingNumber([1,7,4,3,4,7],2));
console.log(getRepeatingNumber([7,4,5,3,5,5,3,4,3,2,1], 3));
console.log(getRepeatingNumber([1,6,4,6,4,6], 4));
Below is the code which will fix your problem I hope.
You need to loop over all array values and find how many time each value occurred and save number of occurrence in a result array because there may be multiple values occurred n number of times. i.e. in your provided array let arr = [1,7,4,3,4,7]; 7 and 4 occurred twice but i am returning result[0] since you might only need first occurred value for n times.
let arr = [1,7,4,3,4,7];
let getRepeatingNumber = function (arr, n) {
let result = [];
arr.forEach((value) => {
if (arr.filter(val => val === value).length === n && !result.includes(value)) {
result.push(value);
}
});
return result.length ? result[0] : -1
}
console.log(getRepeatingNumber(arr, 2)); // 7

Jquery not looping function correctly

I'm new to develop jquery plugin,I have stuck with my function on my function call my plugging repeat same value on elements.I do expect replace all values of my page,respectively
( function ($) {
$.fn.siPrifixx = function (value, options) {
// This is the easiest way to have default options.
var settings = $.extend({
// These are the defaults.
maxDigits: 8,
seperator: true,
decimal: 1,
popUp: true,
index: "tool tip message"
}, options);
console.log(settings.index);
$(this).addClass('tooltip', 'test');
$(this).tooltipster({
theme: 'tooltipster-default',
functionInit: function () {
return value
}
})
// $('.tooltip').prop(settings.index, value);
var number = value;
if (typeof value === 'string') {
var parts = value.split(",");
number = (parseInt(parts.join("")));
}
if (typeof number !== 'undefined' && !isNaN(number)) {
// if the number is alreadey comma seperated convert to number
var n = settings.decimal
// 2 decimal places => 100, 3 => 1000, etc
var decPlace = Math.pow(10, n);
// Enumerate number abbreviations
var abbrev = ["K", "M", "B", "T"];
// Go through the array backwards, so we do the largest first
for (var i = abbrev.length - 1; i >= 0; i--) {
// Convert array index to "1000", "1000000", etc
var size = Math.pow(10, (i + 1) * 3);
// If the number is bigger or equal do the abbreviation
if (size <= number) {
// Here, we multiply by decPlaces, round, and then divide by decPlaces.
// This gives us nice rounding to a particular decimal place.
number = Math.round(number * decPlace / size) / decPlace;
// Handle special case where we round up to the next abbreviation
if ((number == 1000) && (i < abbrev.length - 1)) {
number = 1;
i++;
}
// Add the letter for the abbreviation
number += abbrev[i];
// We are done... stop
break;
}
}
$(this).html(number)
console.log(number)
// return number;
} else {
$(this).html(number)
console.log(number)
// return value;
}
};
}(jQuery));
I'm calling function on a loop like this.
$.each($(plugin.element).find('.widget-data'), function(index, value)
{
var index = $(this).data('index');
var value = data.stats[index];
$('.widget-data').siPrifixx(value,{
decimal:2,
index:index
});
What's the wrong with my code?
When you call $('.widget-data').siPrifixx, you're still addressing all elements with the widget-data class. Since you're already iterating that set, you shouldn't targets all elements in every iteration. Instead call $(this).siPrifixx(...);
Can you try with following code:
$(plugin.element).find('.widget-data').each(function(index)
{
var index_current = $(this).data('index');
var value = data.stats[index_current];
$(this).siPrifixx(value,{
decimal:2,
index:index_current
});
});

Categories