how to make multiple instances in JavaScript - javascript

i was trying to generate random numbers to pick random items from array to update collage of pictures, i noticed that sometimes generated number is same to a previously generated number causing one picture to update 2-3 times so to check this i thought it would be great if i store last generated random number into an variable and make a function that checks if last generated value is equal to
current generated value, it continue to generate numbers till a non-equal value is generated.
it is working fine until i wanted multiple instances ... as variable that stores last value is same, i am getting same numbers.
var bgs = document.getElementById('collage').getElementsByClassName('bg');
var images = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var lv = null;
function get_random(length) {
arr = [];
while (arr.length < 1) {
var cv = Math.floor(Math.random() * length);
if (lv === cv) {
arr = [];
continue;
}
else if (lv !== cv) {
arr.push(cv);
break;
}
}
lv = arr[0];
return arr[0];
}
function update_collage() {
var img = images[get_random(images.length)];
var target = bgs[get_random(bgs.length)];
target.style.background = "url('assets/img/thumbs/" + img + ".jpg') no-repeat center";
target.style.backgroundSize = "cover";
}
window.setInterval(function(){
update_collage();
}, 1200);
how can i correct this, how can i create multiple instances of function or variable

I suggest using a generator function and instantiating it as often as you need:
// Generate pairwise unique random numbers < max:
function* generate_random(max) {
let prev;
while (true) {
let next = Math.floor(Math.random() * max);
if (prev === next) continue;
yield prev = next;
}
}
// Example:
let generators = [generate_random(3), generate_random(3)];
for (let i = 0; i < 10; ++i) {
// Both generators run independently:
console.log(generators[0].next().value, generators[1].next().value);
}
In your code, you could e.g. replace
var img = images[get_random(images.length)];
var target = bgs[get_random(bgs.length)];
with
var img = images[generators[0].next().value];
var target = bgs[generators[1].next().value];
where
var generators = [generate_random(images.length), generate_random(bgs.length)];

Related

Google Sheets Random Copy and Sort with Scripts

What I'm attempting to do is copy a column over and re-sort it. Problem is, it captures all available cells and uses the same space to re-sort, causing blank spaces. The idea is to create tournament match pairings, with the first column being the Roster itself, and following columns being players they will be matched against.
I'd also like to add a line that verifies a name doesn't appear twice on the same row, reshuffling until the column is all unique along each row
This is the code I have so far. I attempted to filter the data by swapping
range2.setValues(shuffleArray(range.getValues()));
for
range2.setValues(shuffleArray(range.getValues().filter(String)));
but this results in a "Number of data rows is 10 when range is 41" error, not verbatim obviously. I'm trying to collapse the blank spaces that are shown in this Screenshot.
I'm sure I can figure out how to expand it by however many matches I wish to generate.
function shuffleRange() {
var sheet = SpreadsheetApp.getActive().getSheetByName('SETUP');
var range = sheet.getRange('A31:A')
var range2 = sheet.getRange('C31:C');
range2.clearContents;
range2.setValues(shuffleArray(range.getValues()));
}
function shuffleArray(array) {
var i, j, temp;
for (i = array.length - 1; i > 0; i--) {
j = Math.floor(Math.random() * (i+1));
temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
EDIT::::: Code has been moved to a test sheet hence different name and ranges, ive adjusted the samples when i moved them of course
function shuffleRange() {
var sheet = SpreadsheetApp.getActive().getSheetByName('Sheet4');
var range = sheet.getRange('A1:A40')
var v = range.getValues().filter(String);
//Match 1
var values = shuffleArray1(v);
while (v.length != [...new Set(values.map(([a]) => a))].length) {
values = shuffleArray1(v);
}
range.offset(0, 1, values.length).setValues(values);
//Match 2
var values2 = shuffleArray2(v);
while (v.length != [...new Set(values2.map(([a]) => a))].length) {
values = shuffleArray2(v);
}
range.offset(0, 2, values.length).setValues(values2);
}
function shuffleArray1(array) {
var i, j, temp;
for (i = array.length - 1; i > 0; i--) {
j = Math.floor(Math.random() * (i+1));
temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
function shuffleArray2(array) {
var u, v, temp;
for (u = array.length - 3; u > 0; u--) {
v = Math.floor(Math.random() * (u+2));
temp = array[u];
array[u] = array[v];
array[v] = temp;
}
return array;
}
Modification points:
I think that range2.clearContents might be range2.clearContent().
In your script, by sheet.getRange('A31:A'), all rows in the sheet are retrieved.
When these points are reflected in your script, how about modifying shuffleRange() as follows?
Modified script:
function shuffleRange() {
var sheet = SpreadsheetApp.getActive().getSheetByName('SETUP');
var lastRow = sheet.getLastRow();
var range = sheet.getRange('A31:A' + lastRow);
var range2 = sheet.getRange('C31:C' + lastRow);
range2.clearContent();
var values = shuffleArray(range.getValues()).filter(String);
range.offset(0, 2, values.length).setValues(values);
}
I'm not sure about the last row of your sheet. So, I proposed the above modification.
Added 1:
From your following new question,
essentially if the row contains a duplicate it has to reshuffle until each row contains a unique name from the original column, to create unique match pairings for tournaments, this will check the whole row, as some tournaments run only 2 matches, some up to 21
In this case, how about the following sample script?
Sample script:
function shuffleRange() {
var sheet = SpreadsheetApp.getActive().getSheetByName('SETUP');
var lastRow = sheet.getLastRow();
var range = sheet.getRange('A31:A' + lastRow);
var range2 = sheet.getRange('C31:C' + lastRow);
range2.clearContent();
var v = range.getValues().filter(String);
var values = shuffleArray(v);
while (v.length != [...new Set(values.map(([a]) => a))].length) {
values = shuffleArray(v);
}
range.offset(0, 2, values.length).setValues(values);
}
In this case, when the duplicated values are included in values, shuffleArray function is run again.
Added 2:
From your following reply,
Unfortunately it produced duplicate lines almost immediately once i duplicate the functions to create a second set of results
I added a new sample so you can see how im trying to expand it across several columns of results, this will create a set number of matches. I will, when done, swap the counter for a cell check so a user can set the match number, but thats later
Sample script:
function shuffleRange() {
var sheet = SpreadsheetApp.getActive().getSheetByName('SETUP');
var range = sheet.getRange('A1:A40');
var v = range.getValues().filter(String);
var createValues = v => {
SpreadsheetApp.flush(); // I'm not sure whether this line is required.
var temp = sheet.getRange(1, 1, 40, sheet.getLastColumn()).getValues();
var existValues = temp[0].map((_, c) => temp.map(r => r[c]).join("")).filter(String);
var values;
do {
values = shuffleArray1(v);
while (v.length != [...new Set(values.map(([a]) => a))].length) {
values = shuffleArray1(v);
}
var check = values.map(([a]) => a).join("");
} while (existValues.some(e => e == check));
return values;
}
var values1 = createValues(v);
range.offset(0, 1, values1.length).setValues(values1);
var values2 = createValues(v);
range.offset(0, 2, values2.length).setValues(values2);
}
In this modification, the new column values are created by checking all existing columns.
Adding to Tanaike's suggestion I've joined your two functions in order to be able to re-shuffle. I'm not as well-versed in coding, and probably there's a more-alike version of your code that also enables the re-shuffling. But you can try this:
function shuffleRange() {
var sheet = SpreadsheetApp.getActive().getSheetByName('SETUP');
var lastRow = sheet.getLastRow()
var range = sheet.getRange('A31:A' + lastRow);
var range2 = sheet.getRange('C31:C' + lastRow);
range2.clearContents;
function shuffleArray() {
var i, j, temp;
var array = range.getValues()
var array2 = range.getValues()
var count= 1;
while (count>0){
count=0
for(i=array.length-1;i>0;i--){
j = Math.floor(Math.random() * (i+1));
temp = array2[i];
array2[i] = array2[j];
array2[j] = temp;
}
for(i=0;i<array.length;i++){
if(array[i].toString() == (array2[i]).toString()){count = count+1}
}}
return array2
}
range2.setValues(shuffleArray())
}
I've make it try tenths of times and never got a duplicate:

JavaScript: Randomly select a limited number of objects from an array to be placed into a second array? [duplicate]

I am working on 'how to access elements randomly from an array in javascript'. I found many links regarding this. Like:
Get random item from JavaScript array
var item = items[Math.floor(Math.random()*items.length)];
But in this, we can choose only one item from the array. If we want more than one elements then how can we achieve this? How can we get more than one element from an array?
Just two lines :
// Shuffle array
const shuffled = array.sort(() => 0.5 - Math.random());
// Get sub-array of first n elements after shuffled
let selected = shuffled.slice(0, n);
DEMO:
Try this non-destructive (and fast) function:
function getRandom(arr, n) {
var result = new Array(n),
len = arr.length,
taken = new Array(len);
if (n > len)
throw new RangeError("getRandom: more elements taken than available");
while (n--) {
var x = Math.floor(Math.random() * len);
result[n] = arr[x in taken ? taken[x] : x];
taken[x] = --len in taken ? taken[len] : len;
}
return result;
}
There is a one-liner unique solution here
array.sort(() => Math.random() - Math.random()).slice(0, n)
lodash _.sample and _.sampleSize.
Gets one or n random elements at unique keys from collection up to the size of collection.
_.sample([1, 2, 3, 4]);
// => 2
_.sampleSize([1, 2, 3], 2);
// => [3, 1]
_.sampleSize([1, 2, 3], 3);
// => [2, 3, 1]
Getting 5 random items without changing the original array:
const n = 5;
const sample = items
.map(x => ({ x, r: Math.random() }))
.sort((a, b) => a.r - b.r)
.map(a => a.x)
.slice(0, n);
(Don't use this for big lists)
create a funcion which does that:
var getMeRandomElements = function(sourceArray, neededElements) {
var result = [];
for (var i = 0; i < neededElements; i++) {
result.push(sourceArray[Math.floor(Math.random()*sourceArray.length)]);
}
return result;
}
you should also check if the sourceArray has enough elements to be returned. and if you want unique elements returned, you should remove selected element from the sourceArray.
Porting .sample from the Python standard library:
function sample(population, k){
/*
Chooses k unique random elements from a population sequence or set.
Returns a new list containing elements from the population while
leaving the original population unchanged. The resulting list is
in selection order so that all sub-slices will also be valid random
samples. This allows raffle winners (the sample) to be partitioned
into grand prize and second place winners (the subslices).
Members of the population need not be hashable or unique. If the
population contains repeats, then each occurrence is a possible
selection in the sample.
To choose a sample in a range of integers, use range as an argument.
This is especially fast and space efficient for sampling from a
large population: sample(range(10000000), 60)
Sampling without replacement entails tracking either potential
selections (the pool) in a list or previous selections in a set.
When the number of selections is small compared to the
population, then tracking selections is efficient, requiring
only a small set and an occasional reselection. For
a larger number of selections, the pool tracking method is
preferred since the list takes less space than the
set and it doesn't suffer from frequent reselections.
*/
if(!Array.isArray(population))
throw new TypeError("Population must be an array.");
var n = population.length;
if(k < 0 || k > n)
throw new RangeError("Sample larger than population or is negative");
var result = new Array(k);
var setsize = 21; // size of a small set minus size of an empty list
if(k > 5)
setsize += Math.pow(4, Math.ceil(Math.log(k * 3) / Math.log(4)))
if(n <= setsize){
// An n-length list is smaller than a k-length set
var pool = population.slice();
for(var i = 0; i < k; i++){ // invariant: non-selected at [0,n-i)
var j = Math.random() * (n - i) | 0;
result[i] = pool[j];
pool[j] = pool[n - i - 1]; // move non-selected item into vacancy
}
}else{
var selected = new Set();
for(var i = 0; i < k; i++){
var j = Math.random() * n | 0;
while(selected.has(j)){
j = Math.random() * n | 0;
}
selected.add(j);
result[i] = population[j];
}
}
return result;
}
Implementation ported from Lib/random.py.
Notes:
setsize is set based on characteristics in Python for efficiency. Although it has not been adjusted for JavaScript, the algorithm will still function as expected.
Some other answers described in this page are not safe according to the ECMAScript specification due to the misuse of Array.prototype.sort. This algorithm however is guaranteed to terminate in finite time.
For older browsers that do not have Set implemented, the set can be replaced with an Array and .has(j) replaced with .indexOf(j) > -1.
Performance against the accepted answer:
https://jsperf.com/pick-random-elements-from-an-array
The performance difference is the greatest on Safari.
If you want to randomly get items from the array in a loop without repetitions you can remove the selected item from the array with splice:
var items = [1, 2, 3, 4, 5];
var newItems = [];
for (var i = 0; i < 3; i++) {
var idx = Math.floor(Math.random() * items.length);
newItems.push(items[idx]);
items.splice(idx, 1);
}
console.log(newItems);
ES6 syntax
const pickRandom = (arr,count) => {
let _arr = [...arr];
return[...Array(count)].map( ()=> _arr.splice(Math.floor(Math.random() * _arr.length), 1)[0] );
}
I can't believe that no one didn't mention this method, pretty clean and straightforward.
const getRnd = (a, n) => new Array(n).fill(null).map(() => a[Math.floor(Math.random() * a.length)]);
Array.prototype.getnkill = function() {
var a = Math.floor(Math.random()*this.length);
var dead = this[a];
this.splice(a,1);
return dead;
}
//.getnkill() removes element in the array
//so if you like you can keep a copy of the array first:
//var original= items.slice(0);
var item = items.getnkill();
var anotheritem = items.getnkill();
Here's a nicely typed version. It doesn't fail. Returns a shuffled array if sample size is larger than original array's length.
function sampleArr<T>(arr: T[], size: number): T[] {
const setOfIndexes = new Set<number>();
while (setOfIndexes.size < size && setOfIndexes.size < arr.length) {
setOfIndexes.add(randomIntFromInterval(0, arr.length - 1));
}
return Array.from(setOfIndexes.values()).map(i => arr[i]);
}
const randomIntFromInterval = (min: number, max: number): number =>
Math.floor(Math.random() * (max - min + 1) + min);
In this answer, I want to share with you the test that I have to know the best method that gives equal chances for all elements to have random subarray.
Method 01
array.sort(() => Math.random() - Math.random()).slice(0, n)
using this method, some elements have higher chances comparing with others.
calculateProbability = function(number=0 ,iterations=10000,arraySize=100) {
let occ = 0
for (let index = 0; index < iterations; index++) {
const myArray= Array.from(Array(arraySize).keys()) //=> [0, 1, 2, 3, 4, ... arraySize]
/** Wrong Method */
const arr = myArray.sort(function() {
return val= .5 - Math.random();
});
if(arr[0]===number) {
occ ++
}
}
console.log("Probability of ",number, " = ",occ*100 /iterations,"%")
}
calculateProbability(0)
calculateProbability(0)
calculateProbability(0)
calculateProbability(50)
calculateProbability(50)
calculateProbability(50)
calculateProbability(25)
calculateProbability(25)
calculateProbability(25)
Method 2
Using this method, the elements have the same probability:
const arr = myArray
.map((a) => ({sort: Math.random(), value: a}))
.sort((a, b) => a.sort - b.sort)
.map((a) => a.value)
calculateProbability = function(number=0 ,iterations=10000,arraySize=100) {
let occ = 0
for (let index = 0; index < iterations; index++) {
const myArray= Array.from(Array(arraySize).keys()) //=> [0, 1, 2, 3, 4, ... arraySize]
/** Correct Method */
const arr = myArray
.map((a) => ({sort: Math.random(), value: a}))
.sort((a, b) => a.sort - b.sort)
.map((a) => a.value)
if(arr[0]===number) {
occ ++
}
}
console.log("Probability of ",number, " = ",occ*100 /iterations,"%")
}
calculateProbability(0)
calculateProbability(0)
calculateProbability(0)
calculateProbability(50)
calculateProbability(50)
calculateProbability(50)
calculateProbability(25)
calculateProbability(25)
calculateProbability(25)
The correct answer is posted in in the following link: https://stackoverflow.com/a/46545530/3811640
2020
non destructive functional programing style, working in a immutable context.
const _randomslice = (ar, size) => {
let new_ar = [...ar];
new_ar.splice(Math.floor(Math.random()*ar.length),1);
return ar.length <= (size+1) ? new_ar : _randomslice(new_ar, size);
}
console.log(_randomslice([1,2,3,4,5],2));
EDIT: This solution is slower than others presented here (which splice the source array) if you want to get only a few elements. The speed of this solution depends only on the number of elements in the original array, while the speed of the splicing solution depends on the number of elements required in the output array.
If you want non-repeating random elements, you can shuffle your array then get only as many as you want:
function shuffle(array) {
var counter = array.length, temp, index;
// While there are elements in the array
while (counter--) {
// Pick a random index
index = (Math.random() * counter) | 0;
// And swap the last element with it
temp = array[counter];
array[counter] = array[index];
array[index] = temp;
}
return array;
}
var arr = [0,1,2,3,4,5,7,8,9];
var randoms = shuffle(arr.slice(0)); // array is cloned so it won't be destroyed
randoms.length = 4; // get 4 random elements
DEMO: http://jsbin.com/UHUHuqi/1/edit
Shuffle function taken from here: https://stackoverflow.com/a/6274398/1669279
I needed a function to solve this kind of issue so I'm sharing it here.
const getRandomItem = function(arr) {
return arr[Math.floor(Math.random() * arr.length)];
}
// original array
let arr = [4, 3, 1, 6, 9, 8, 5];
// number of random elements to get from arr
let n = 4;
let count = 0;
// new array to push random item in
let randomItems = []
do {
let item = getRandomItem(arr);
randomItems.push(item);
// update the original array and remove the recently pushed item
arr.splice(arr.indexOf(item), 1);
count++;
} while(count < n);
console.log(randomItems);
console.log(arr);
Note: if n = arr.length then basically you're shuffling the array arr and randomItems returns that shuffled array.
Demo
Here's an optimized version of the code ported from Python by #Derek, with the added destructive (in-place) option that makes it the fastest algorithm possible if you can go with it. Otherwise it either makes a full copy or, for a small number of items requested from a large array, switches to a selection-based algorithm.
// Chooses k unique random elements from pool.
function sample(pool, k, destructive) {
var n = pool.length;
if (k < 0 || k > n)
throw new RangeError("Sample larger than population or is negative");
if (destructive || n <= (k <= 5 ? 21 : 21 + Math.pow(4, Math.ceil(Math.log(k*3) / Math.log(4))))) {
if (!destructive)
pool = Array.prototype.slice.call(pool);
for (var i = 0; i < k; i++) { // invariant: non-selected at [i,n)
var j = i + Math.random() * (n - i) | 0;
var x = pool[i];
pool[i] = pool[j];
pool[j] = x;
}
pool.length = k; // truncate
return pool;
} else {
var selected = new Set();
while (selected.add(Math.random() * n | 0).size < k) {}
return Array.prototype.map.call(selected, i => pool[i]);
}
}
In comparison to Derek's implementation, the first algorithm is much faster in Firefox while being a bit slower in Chrome, although now it has the destructive option - the most performant one. The second algorithm is simply 5-15% faster. I try not to give any concrete numbers since they vary depending on k and n and probably won't mean anything in the future with the new browser versions.
The heuristic that makes the choice between algorithms originates from Python code. I've left it as is, although it sometimes selects the slower one. It should be optimized for JS, but it's a complex task since the performance of corner cases is browser- and their version-dependent. For example, when you try to select 20 out of 1000 or 1050, it will switch to the first or the second algorithm accordingly. In this case the first one runs 2x faster than the second one in Chrome 80 but 3x slower in Firefox 74.
Sampling with possible duplicates:
const sample_with_duplicates = Array(sample_size).fill().map(() => items[~~(Math.random() * items.length)])
Sampling without duplicates:
const sample_without_duplicates = [...Array(items.length).keys()].sort(() => 0.5 - Math.random()).slice(0, sample_size).map(index => items[index]);
Since without duplicates requires sorting the whole index array first, it is considerably slow than with possible duplicates for big items input arrays.
Obviously, the max size of without duplicates is <= items.length
Check this fiddle: https://jsfiddle.net/doleron/5zw2vequ/30/
It extracts random elements from srcArray one by one while it get's enough or there is no more elements in srcArray left for extracting.
Fast and reliable.
function getNRandomValuesFromArray(srcArr, n) {
// making copy to do not affect original srcArray
srcArr = srcArr.slice();
resultArr = [];
// while srcArray isn't empty AND we didn't enough random elements
while (srcArr.length && resultArr.length < n) {
// remove one element from random position and add this element to the result array
resultArr = resultArr.concat( // merge arrays
srcArr.splice( // extract one random element
Math.floor(Math.random() * srcArr.length),
1
)
);
}
return resultArr;
}
Here's a function I use that allows you to easily sample an array with or without replacement:
// Returns a random sample (either with or without replacement) from an array
const randomSample = (arr, k, withReplacement = false) => {
let sample;
if (withReplacement === true) { // sample with replacement
sample = Array.from({length: k}, () => arr[Math.floor(Math.random() * arr.length)]);
} else { // sample without replacement
if (k > arr.length) {
throw new RangeError('Sample size must be less than or equal to array length when sampling without replacement.')
}
sample = arr.map(a => [a, Math.random()]).sort((a, b) => {
return a[1] < b[1] ? -1 : 1;}).slice(0, k).map(a => a[0]);
};
return sample;
};
Using it is simple:
Without Replacement (default behavior)
randomSample([1, 2, 3], 2) may return [2, 1]
With Replacement
randomSample([1, 2, 3, 4, 5, 6], 4) may return [2, 3, 3, 2]
var getRandomElements = function(sourceArray, requiredLength) {
var result = [];
while(result.length<requiredLength){
random = Math.floor(Math.random()*sourceArray.length);
if(result.indexOf(sourceArray[random])==-1){
result.push(sourceArray[random]);
}
}
return result;
}
const items = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'I', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 1, 2, 3, 4, 5];
const fetchRandomArray = ({pool=[], limit=1})=>{
let query = []
let selectedIndices = {}
while(query.length < limit){
const index = Math.floor(Math.random()*pool.length)
if(typeof(selectedIndices[index])==='undefined'){
query.push(items[index])
selectedIndices[index] = index
}
}
console.log(fetchRandomArray({pool:items, limit:10})
2019
This is same as Laurynas MaliĊĦauskas answer, just that the elements are unique (no duplicates).
var getMeRandomElements = function(sourceArray, neededElements) {
var result = [];
for (var i = 0; i < neededElements; i++) {
var index = Math.floor(Math.random() * sourceArray.length);
result.push(sourceArray[index]);
sourceArray.splice(index, 1);
}
return result;
}
Now to answer original question "How to get multiple random elements by jQuery", here you go:
var getMeRandomElements = function(sourceArray, neededElements) {
var result = [];
for (var i = 0; i < neededElements; i++) {
var index = Math.floor(Math.random() * sourceArray.length);
result.push(sourceArray[index]);
sourceArray.splice(index, 1);
}
return result;
}
var $set = $('.someClass');// <<<<< change this please
var allIndexes = [];
for(var i = 0; i < $set.length; ++i) {
allIndexes.push(i);
}
var totalRandom = 4;// <<<<< change this please
var randomIndexes = getMeRandomElements(allIndexes, totalRandom);
var $randomElements = null;
for(var i = 0; i < randomIndexes.length; ++i) {
var randomIndex = randomIndexes[i];
if($randomElements === null) {
$randomElements = $set.eq(randomIndex);
} else {
$randomElements.add($set.eq(randomIndex));
}
}
// $randomElements is ready
$randomElements.css('backgroundColor', 'red');
Here is the most correct answer and it will give you Random + Unique elements.
function randomize(array, n)
{
var final = [];
array = array.filter(function(elem, index, self) {
return index == self.indexOf(elem);
}).sort(function() { return 0.5 - Math.random() });
var len = array.length,
n = n > len ? len : n;
for(var i = 0; i < n; i ++)
{
final[i] = array[i];
}
return final;
}
// randomize([1,2,3,4,5,3,2], 4);
// Result: [1, 2, 3, 5] // Something like this
items.sort(() => (Math.random() > 0.5 ? 1 : -1)).slice(0, count);

Write a function that will return the two number array combination with value summed at 5. I can't get all the opportunities

A job posting wants me to write a an answer to a question which if I solve I am eligible for the next rownd.
Write a function that will return the array combination with value
summed at 5. Important: Use only one "for" loop. Example: var
rand_array = [1,3,5,2,4,6]; var target_sum = 5; Output = [1,4], [5],
[3,2], [2,3], [4,1];
I attempted to find a solution online and stumbled upon this:
https://www.geeksforgeeks.org/given-an-array-a-and-a-number-x-check-for-pair-in-a-with-sum-as-x/ as StackOverflow wants you to do your own research first.
However, when trying to convert it to JS, all that happened was that it returned just one case where it worked. I need it to return every case where it worked. I then make some other changes and it just stopped working now.
var ra = [1,3,5,2,4,6];
var target = 5
ra.sort();
lower = 0;
higher = ra.length -1;
var solutions = [];
var result;
while (lower < higher) {
if (ra[lower] + ra[higher] === target){
result = [ra[lower], ra[higher]];
solutions.push(result);
}
else if (ra[lower] + ra[higher] > target){
higher--;
}
else {
lower++;
}
}
return solutions;
}
console.log(solutions);
Can someone write an example for me?
Your code does not work at all at the moment because it doesn't always increment lower or higher (resulting in an infinite loop). It also has greater complexity than necessary (.sort has complexity O(n log n)), but the instructions indicate that low complexity is important. The array also isn't being sorted numerically. (To sort numerically, use .sort((a, b) => a - b))
If you want a solution with the least complexity possible, O(n), while iterating, create an object. On every iteration, check to see if the object has a key for which the current number would sum with to 5 (eg, when iterating on 1, look to see if a 4 property exists on the object). If one is found, add it to the solutions. Otherwise, set a new key on the object:
const ra = [1, 3, 5, 2, 4, 6];
const target = 5;
const solutions = [];
const obj = {};
for (const item of ra) {
const match = target - item;
if (obj[match]) {
solutions.push([item, match]);
delete obj[match];
} else {
obj[item] = true;
}
}
console.log(solutions);
If there may be repeated numbers, then store a count in the object instead of just true:
const ra = [1, 1, 1, 3, 5, 2, 4, 6, 4, 4];
const target = 5;
const solutions = [];
const obj = {};
for (const item of ra) {
const match = target - item;
if (obj[match]) {
solutions.push([item, match]);
obj[match]--;
} else {
obj[item] = (obj[item] || 0) + 1;
}
}
console.log(solutions);
I don't want to write the actual answer because its a job assignment, but I will say that a simple 2 loop function is the obvies solution, than try to think not checking the array from the top and the bottum, rether like the formation of a loop in a loop.
hint :
let i = 0;
let j = 0;
while (i < arr.langth) {
...
if (j < arr.langth) {
j++;
} else {
j = 0;
i++;
}
}
Your code as it stands just does not work at all. CertainPerformance has a solid answer, except that it doesn't do the task as required, i.e. treat the same numbers in a different order as different or get the values which are equal to target as solutions. Here is my solution to your problem:
const ra = [1,3,5,2,4,6];
const target = 5
function getSumsOfTarget(ra, target){
ra.sort();
lower = 0;
higher = ra.length -1;
const solutions = [];
let result;
while (lower < ra.length && higher >= 0) {
const sum = ra[lower] + ra[higher];
if (ra[lower] === target) {
result = [ra[lower]];
solutions.push(result);
break;
}
if (sum === target){
result = [ra[lower], ra[higher]];
solutions.push(result);
lower++;
}
else if (sum > target){
higher--;
}
else {
lower++;
}
}
return solutions;
}
console.log(getSumsOfTarget(ra, target));

looping infinitely through multiple arrays at the same time until a condition is met

I'm trying to find least common multiples of the two numbers given [3,5] and return only the number that's divisible by all the number in the range of the two numbers... for example:
The given array of two numbers --> let arr = [3,5];
The first number Multiples should be as follow:
[3,6,9,12,15,18,21,24,27,30,33,36,39,42,45,48,51,54,57,60];
The second number Multiples should be as follow:
[5,10,15,20,25,30,35,40,45,50,55,60];
The Least common multiples should be as follows:
[15,30,45,60];
the only that is divisible by all the number in the range is 60.
This is my approach to solve this problem but I want to know what's wrong with my code below (PLEASE EXPLAIN 'cause I'm tired of guessing):
let arr = [3, 5];
let arrRange = []; // [3, 4, 5]
// creating a loop to create the range
for (var i = arr[0]; i <= arr[1]; i++) {
arrRange.push(i);
}
let f = arr[0], s = arr[1], c = 0, result = 0, firstMultiples = [], secondMultiples = [], leastCommonMultiples = [];
// This function is made if the number least Common number is divisible by all the numbers in the "arrRange"
function isDivisible(num) {
for(var i = 0; i < arrRange.length; i++) {
if(num % arrRange[i] != 0) {
return false;
}
}
return true;
}
while(true) {
firstMultiples.push(f);
secondMultiples.push(s);
f = f + arr[0];
s = s + arr[1];
let vals = secondMultiples.values();
for(let val of vals){
if( firstMultiples.includes(val) ) {
leastCommonMultiples.push(val);
}
}
let cmlVals = leastCommonMultiples.values();
for(let cmlVal of cmlVals){
if(isDivisible(cmlVal)) {
result += cmlVal;
break;
}
}
c++;
}
console.log(result);
To fix it, change the while-loop from while (true) {/*code*/}; to
while(isDivisible(cmlVal) == true) {/*code*/}; and remove the
if(isDivisible(cmlVal)) {/*code*/ break;}.

Javascript: Adding final element to array after for loop completes

Explanation of Desired Results
My source is over 30,000 lines of very structured text with incrementing front numbers followed by incrementing back numbers and separated by a colon. Stripping out the non-essentials, I am left with the following sourceArray, truncated for convenience :)
sourceArray = ["001:001", "001:002", "001:003",
"002:001", "002:002",
"003:001", "003:002"];
I am trying to count how many back numbers for each front number and push that to an array. In pseudocode, my final results should look like this:
myArray[totalNumberOf_001_Items, totalNumberOf_002_Items, totalNumberOf_003_Items]
Which in my simple example should give me a final value of:
[3, 2, 2]
Problem and Question
My for loop ends at the end of my data and I am therefore one element short in my array.
How do I make an "extra pass" through the loop or is there another way to get the final element pushed to my array?
var sourceArray = ["001:001", "001:002", "001:003",
"002:001", "002:002",
"003:001", "003:002"
];
var myArray = [];
var frontCounter = 1;
var backCounter = 1;
for (var i = 0; i < sourceArray.length; i++) {
var text = sourceArray[i];
var front = text.substr(0, 3);
front = Number(front);
var back = text.substr(4, 3);
back = Number(back);
if (front == frontCounter) {
backCounter++;
} else {
myArray.push(backCounter - 1);
backCounter = 2;
frontCounter++;
}
}
console.log(myArray); // result [3, 2]
You could use an object like below to keep track of how many times the the piece of text appear, the text would be the keys and as value the number of times they appear. From that you can build you array
var sourceArray = ["001:001", "001:002", "001:003",
"002:001", "002:002",
"003:001", "003:002"];
var frontEncounters = {};
function updateFrontEncounters(frontEncounters, front){
var keys = Object.keys(frontEncounters);
if(keys.indexOf(front) == -1)
{
frontEncounters[front] = 1;
}
else
{
frontEncounters[front] += 1;
}
}
for(var item in sourceArray){
var text = sourceArray[item];
var front = text.substr(0, 3);
var back = text.substr(4, 3);
updateFrontEncounters(frontEncounters, front);
}
var keys = Object.keys(frontEncounters);
var myArr = [];
for(var key in keys)
{
myArr.push(frontEncounters[keys[key]])
}
console.log(myArr);
Use an object to store the "front" numbers along with their count.
for (var i = 0; i < sourceArray.length; i++) {
var num = sourceArray[i].slice(0,3);
counts[num] = counts[num] ? counts[num]+1 : 1;
}
Once done, you can very easily convert that to an array:
var result = Object.keys(counts).map(function (key) {
return counts[key];
});
With ES-2017, it is even easier:
var result = Object.values(counts)
Working Snippet:
var sourceArray = ["001:001", "001:002", "001:003",
"002:001", "002:002",
"003:001", "003:002"];
var counts = {};
for (var i = 0; i < sourceArray.length; i++) {
var num = sourceArray[i].slice(0,3);
counts[num] = counts[num] ? counts[num]+1 : 1;
}
console.log(counts);
var result = Object.keys(counts).map(function (key) {
return counts[key];
});
console.log(result);
// ES-2017
//console.log(Object.values(counts));
Here's an alternative that you can use so that you don't have to go through the entire source of lines (30,000) . Use a while loop so that you can break as soon as you reach a 0; use Map to store the unique number by making the index/frontnumber the key and make its value an object that serves as a counter to keep track of it's total. If the key exists, update the total; if it doesn't, create a new counter object. Then just return the Map by transforming it into the desired array by map'ing it to an array with only the totals.
var sourceArray = ["001:001", "001:002", "001:003",
"002:001", "002:002",
"003:001", "003:002"
];
function getTotal(sourceArray) {
let count = new Map();
let update = item => count.get(item).total++;
let create = item => count.set(item, {total: 1});
const getItem = index => {
let text = sourceArray[index];
return text.substr(0, 3);
}
let index = -1;
let start = 0;
while (index != 0 && start < sourceArray.length) {
index = getItem(start++);
count.has(index) ? update(index) : create(index);
}
return [...count.values()].map(item => item.total);
}
console.log(getTotal(sourceArray));

Categories

Resources