sample array n^2 times without the same number occurring twice in a row and not repeating every n - javascript

I'm trying to write a function that has the arguments of the array to sample arr and the number of samples, size (which is sqaured) and randomly samples from the original array:
arr = [1,2,3,4]
single_number = (x) => {
return x[Math.floor(Math.random()*x.length)];
}
randomize = (arr, size) => {
return Array(size*size).fill().map(x => single_number(arr))
}
randomize(arr, 5)
I want to add the additional requirements to my randomize function:
no number shows up twice in a row
make sure every sizeth item is not the same as the one before it
For example
randomize([1,2,3,4], 2)
[2,4,3,2,4,1,1,2,2,1,4,1,1,1,3,1,1,4,4,1,3,3,2,2,3]
CASE (1)
[
2,4,3,2,4,
2,1,
2,2, // illegal!
1,4,
1,1,1, // illegal!
3,
1,1, // illegal!
4,4, // illegal!
1,
3,3, // illegal!
2,2, // illegal!
3
]
CASE (2)
[
2,4,3,2,4, [0] === 2
2,1,2,2,1, [5] === 2 // illegal!
4,1,1,1,3,
1,1,4,4,1,
3,3,2,2,3
]
I'm trying to use functional programming and avoid a for loop if possible since I think I can do this with a nested for loop?

Well, this isn't as pretty as one would hope, but I think it accomplishes the objective: Iterate size^2 times and choose random elements from the input, taking care to exclude the last value and last nth value chosen...
const randomize = (array, size) => {
const rand = () => Math.floor(Math.random() * array.length);
const randExcept = exclude => {
let v = array[rand()];
while (exclude.includes(v)) v = array[rand()];
return v;
}
const indexes = Array.from(Array(size*size).keys());
let lastV = null, nthV = null;
return indexes.map(i => {
let exclude = nthV!==null && i%size===1 ? [lastV, nthV] : [lastV];
let v = randExcept(exclude);
lastV = v;
if (i%size===1) nthV = v;
return v;
})
}
console.log( JSON.stringify(randomize([1,2,3,4], 2)) )
This defines nth values by the count into the array, so for size===2, the constraint is that every second element (indexes 1,3,5...) can't be equal to the prior second element.

I'd probably do something like this:
const values = [1,2,3,4]
function randomize(values, size) {
let prev;
let prevNth;
return Array(size*size).fill().map( randomNumber );
function randomNumber(_,i) {
let value, ok;
do {
value = values[ Math.floor( Math.random() * values.length ) ];
ok = value != prev;
if ( i % size === 0) {
ok = ok && value != prevNth;
prevNth = value;
}
prev = value;
} while (!ok);
return value;
}
}
arr = randomize(values, 5)
console.log(JSON.stringify(arr));
Or this, using a generator to generate the appropriately sized stream of randomness:
const values = [1,2,3,4];
const arr1 = Array.from( randomValues(5,values) );
console.log(JSON.stringify(arr1));
function *randomValues(n, values) {
const limit = n*n;
let prev, prevNth;
for ( let i = 0 ; i < limit ; ++i ) {
const isNthValue = i % n === 0;
const value = randomValue( values, prev, isNthValue ? prevNth : undefined );
yield value;
prev = value;
prevNth = isNthValue ? value : prevNth;
}
}
function randomValue(values, test1, test2 ) {
let value;
do {
value = values[ Math.floor( Math.random() * values.length ) ];
} while (value === test1 || value === test2 );
return value;
}

Related

JavaScript sum array using recursion

My task is to sum elements of an array and add it to second parameter (number) using recursion.
Return only gives me last value of sum. I would appreciate any feedback :)
const getArraySum = (numbersArray, initialValue) => {
// let sum = initialValue
// for (let i = 0; i < numbersArray.length; i++) {
// sum += numbersArray[i]
// } return sum
if (numbersArray.length === 0 ) {
return initialValue
} else {
let sum = 0
sum += numbersArray[numbersArray.length-1]
console.log (numbersArray)
numbersArray.pop()
console.log (sum)
getArraySum (numbersArray)
return sum + initialValue
}
};
const result1 = getArraySum([4,7,10], 5)
console.log (result1)
You're ignoring the return value of the recursive call to getArraySum. Instead, you should add it to the returned value:
const getArraySum = (numbersArray, initialValue) => {
if (numbersArray.length === 0) {
return initialValue
}
return numbersArray.pop() + getArraySum(numbersArray, initialValue);
};
const result = getArraySum([4,7,10], 5)
console.log (result)
Note that the initialValue should only be taken into consideration once, in the end condition where the array is empty.
The idea is to split an array into head (=the first element or null for an empty array) and tail (everything else). Then, establish that the sum is head + sum(tail):
let sum = ([head = null, ...tail]) =>
head === null ? 0 : head + sum(tail)
console.log(sum([1,2,3,4]))
Having an initial value is a silly requirement, but it goes the same way:
let sum = ([head = null, ...tail], init = 0) =>
init + (head === null ? 0 : head + sum(tail))
console.log(sum([1, 2, 3, 4], 100))
You could take the single values a summing variable and return immediately if the array is empty.
Otherwise return the function call with a new shorter array and hte sum of sum and the first element.
const
getSum = (numbers, sum = 0) => {
if (!numbers.length) return sum;
return getSum(numbers.slice(1), sum + numbers[0]);
};
console.log (getSum([4, 7, 10], 5));
As long as the array has an element, return last element (pop) plus getArraySum([rest of the array]), otherwise return initial value:
const getArraySum = (numbersArray, initialValue) => {
if (numbersArray.length === 0 ) {
return initialValue
} else {
return numbersArray.pop() + getArraySum(numbersArray, initialValue);
}
};
const result1 = getArraySum([4,7,10], 5)
console.log (result1)

Is there way to pass values dynamically through array instead of using indexes

[image][1] [1]: https://i.stack.imgur.com/W7bNt.png
Right now it works like this:
1- when key's index 0 do this,
2- when key's index 1 do this,
3- when key's index 2 do this.
but I want to add min and max value for objectVal array that can fix (for stance key[min], n , key[max]) but nth values in between, like it can add new keys and values in slider dynamically instead of calling its indexs and adding new conditional operators.
const sliders = document.querySelectorAll(".allSliders");
let result = document.querySelector(".result");
let totalNumber = 0;
let values = [];
let sliderNums = [];
let objectVal = [
{
keys: [0, 500,1000],
values: [0.0045, 0.0044,0.0043]
},
{
keys: [0, 500,1000],
values: [0.0045, 0.0044,0.0043]
},
{
keys: [0, 500,1000],
values: [0.0045, 0.0044,0.0043]
},
];
sliders.forEach((sliderNumber, id) => {
sliderNumber.addEventListener("input", () => {
let action =
objectVal[id].keys[3] - objectVal[id].keys[3];
let maxLine = objectVal[id].keys[3] + action;
let getInformation = maxLine - action * 0.666;
sliderNumber.setAttribute("max", maxLine);
let currentRange = parseInt(sliderNumber.value);
sliderNums[id] = currentRange;
function line(value, currentVal, obj)
{
values[id] = currentVal * obj;
sliderNumber.nextElementSibling.querySelector(
".tooltiptext"
).textContent = `(${currentVal} * ${obj})`;
sliderNumber.nextElementSibling.querySelector(".dollar").textContent =
values[id].toFixed(2);
}
if (
currentRange > objectVal[id].keys[0] &&
currentRange <= objectVal[id].keys[3] )
{
line(values[id], currentRange, objectVal[id].values[0])
}
else if (
currentRange > objectVal[id].keys[3] &&
currentRange <= objectVal[id].keys[3]
) {
line(values[id], currentRange, objectVal[id].values[3])
}
else {
line(values[id], currentRange, objectVal[id].values[3])
}```

Serializing Array of Many Duplicates

So I have a series of arrays, each of which are 2500 long, and I need to serialize and store all them in very limited space.
Since I have many duplicates, I wanted to cut them down to something like below.
[0,0,0,0,2,7,3,3,0,0,0,0,0,0,0,0,0]
// to
[0x4,2,7,3x2,0x9]
I wrote a couple one-liners (utilising Lodash' _.repeat) to convert to and from this pattern, however converting to doesn't seem to work in most/all cases.
let serialized = array.toString().replace(/((?:(\d)+,?)((?:\2+,?){2,}))/g, (m, p1, p2) => p2 + 'x' + m.replace(/,/g, '').length);
let parsed = serialized.replace(/(\d+)x(\d+),?/g, (z, p1, p2) => _.repeat(p1 + ',', +p2)).split(',');
I don't know why it doesn't work. It may be due to some of the numbers in the array. Eye-balling, the largest one is 4294967295, however well over 90% is just 0.
What am I missing in my RegEx that's preventing it from working correctly? Is there a simpler way that I'm too blind to see?
I'm fairly confident with converting it back from the serialized state, just need a hand getting it to the state.
Straight forward and simple serialization:
let serialize = arr => {
const elements = [];
const counts = []
let last = undefined;
[0,0,0,0,2,7,3,3,0,0,0,0,0,0,0,0,0].forEach((el,i,arr)=>{
if (el!==last) {
elements.push(el);
counts.push(1);
} else {
counts[counts.length-1]++;
}
last = el;
})
return elements.map((a,i)=>counts[i]>1?`${a}x${counts[i]}`:a).join(",");
};
console.log(serialize([0,0,0,0,2,7,3,3,0,0,0,0,0,0,0,0,0]));
UPDATE
Pure functional serialize one:
let serialize = arr => arr
.reduce((memo, element, i) => {
if (element !== arr[i - 1]) {
memo.push({count: 1, element});
} else {
memo[memo.length - 1].count++;
}
return memo;
},[])
.map(({count, element}) => count > 1 ? `${count}x${element}` : element)
.join(",");
console.log(serialize([0,0,0,0,2,7,3,3,0,0,0,0,0,0,0,0,0]));
Pure functional deserialize:
const deserialize = str => str
.split(",")
.map(c => c.split("x").reverse())
.reduce((memo, [el, count = 1]) => memo.concat(Array(+count).fill(+el)), []);
console.log(deserialize("4x0,2,7,2x3,9x0"))
In order to avoid using .reverse() in this logic, I'd recommend to change serialization from 4x0 to 0x4
Try this
var arr = [0,0,0,0,2,7,3,3,0,0,0,0,0,0,0,0,0];
var finalArray = []; //array into which count of values will go
var currentValue = ""; //current value for comparison
var tmpArr = []; //temporary array to hold values
arr.forEach( function( val, index ){
if ( val != currentValue && currentValue !== "" )
{
finalArray.push( tmpArr.length + "x" + tmpArr[0] );
tmpArr = [];
}
tmpArr.push(val);
currentValue = val;
});
finalArray.push( tmpArr.length + "x" + tmpArr[0] );
console.log(finalArray);
Another version without temporary array
var arr = [0, 0, 0, 0, 2, 7, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0];
var finalArray = []; //array into which count of values will go
var tmpCount = 0; //temporary variable to hold count
arr.forEach(function(val, index) {
if ( (val != arr[ index - 1 ] && index !== 0 ) )
{
finalArray.push(tmpCount + "x" + arr[ index - 1 ] );
tmpCount = 0;
}
tmpCount++;
if ( index == arr.length - 1 )
{
finalArray.push(tmpCount + "x" + arr[ index - 1 ] );
}
});
console.log(finalArray);
Do not use RegEx. Just use regular logic. I recommend array.reduce for this job.
const arr1 = [0,0,0,0,2,7,3,3,0,0,0,0,0,0,0,0,0]
const arr2 = ['0x4','2','7','3x2','0x9'];
const compact = arr => {
const info = arr.reduce((c, v) =>{
if(c.prevValue !== v){
c.order.push(v);
c.count[v] = 1;
c.prevCount = 1;
c.prevValue = v;
} else {
c.prevCount = c.prevCount + 1;
c.count[v] = c.count[v] + 1;
};
return c;
},{
prevValue: null,
prevCount: 0,
count: {},
order: []
});
return info.order.map(v => info.count[v] > 1 ? `${v}x${info.count[v]}` : `${v}`);
}
const expand = arr => {
return arr.reduce((c, v) => {
const split = v.split('x');
const value = +split[0];
const count = +split[1] || 1;
Array.prototype.push.apply(c, Array(count).fill(value));
return c;
}, []);
}
console.log(compact(arr1));
console.log(expand(arr2));
This is a typical reducing job. Here is your compress function done in just O(n) time..
var arr = [0,0,0,0,2,7,3,3,0,0,0,0,0,0,0,0,0],
compress = a => a.reduce((r,e,i,a) => e === a[i-1] ? (r[r.length-1][1]++,r) : (r.push([e,1]) ,r),[]);
console.log(JSON.stringify(compress(arr)));
since the motivation here is to reduce the size of the stored arrays, consider using something like gzip-js to compress your data.

Finding a Single Integer in an array using Javascript

I was able to pull all single integers after 'reduce', but not working when there's all duplicates and output should be 0, not hitting my else or else if - code keeps outputting 0 vs the single integers
var singleNumber = function(nums) {
var sorted_array = nums.sort();
for (var i=0; i < sorted_array.length; i++){
var previous = sorted_array[i-1];
var next = sorted_array[i+1];
var singles = {key: 0};
var singlesArray = [];
if (sorted_array[i] !== previous && sorted_array[i] !== next){
singlesArray.push(sorted_array[i]);
singlesArray.reduce(function(singles, key){
singles.key = key;
//console.log('key', key);
return singles.key;
},{});
}
else if(singlesArray.length === 0) {
singles.key = 0;
return singles.key;
}
}
console.log('singles.key', singles.key);
return singles.key;
};
console.log(singleNumber([2,1,3,4,4]));
// tests
const n1 = [1,2,3,4,4] //[1,2,3]
const n2 = [1] //[1]
const n3 = [1,1] //0
const n4 = [1,1,1] //0
const n5 = [1,5,3,4,5] //[1,3,4]
const n6 = [1,2,3,4,5] //[1,2,3,4,5]
const n7 = [1,5,3,4,5,6,7,5] //[1,3,4,6,7]
const singleNumber = numbers => {
const reducer = (acc, val) => {
// check to see if we have this key
if (acc[val]) {
// yes, so we increment its value by one
acc[val] = acc[val] + 1
} else {
// no, so it's a new key and we assign 1 as default value
acc[val] = 1
}
// return the accumulator
return acc
}
// run the reducer to group the array into objects to track the count of array elements
const grouped = numbers.reduce(reducer, {})
const set = Object.keys(grouped)
// return only those keys where the value is 1, if it's not 1, we know its a duplicate
.filter(key => {
if (grouped[key] == 1) {
return true
}
})
// object.keys makes our keys strings, so we need run parseInt to convert the string back to integer
.map(key => parseInt(key))
// check to array length. If greater than zero, return the set. If it is zero, then all the values were duplicates
if (set.length == 0) {
return 0
} else {
// we return the set
return set
}
}
console.log(singleNumber(n7))
https://jsbin.com/sajibij/edit?js,console

Javascript: take every nth Element of Array

I get an Array with an unknown Number of data.
But I only have an predefined amount of data to be shown/store.
How can I take every nth Element of the initial Array and reduce it in JavaScript?
Eg.: I get an Array with size=10000, but are only able to show n=2k Elements.
I tried it like that:
delta= Math.round(10*n/size)/10 = 0.2 -> take every 5th Element of the initial Array.
for (i = 0; i < oldArr.length; i++) {
arr[i] = oldArr[i].filter(function (value, index, ar) {
if (index % delta != 0) return false;
return true;
});
}
With 0.2 it´s always 0, but with some other deltas (0.3) it is working. Same for delta=0.4, i works, but every second Element is taken with that. What can I do to get this to work?
Maybe one solution :
avoid filter because you don't want to loop over 10 000 elements !
just access them directly with a for loop !
var log = function(val){document.body.innerHTML+='<div></pre>'+val+'</pre></div>'}
var oldArr = [0,1,2,3,4,5,6,7,8,9,10]
var arr = [];
var maxVal = 5;
var delta = Math.floor( oldArr.length / maxVal );
// avoid filter because you don't want
// to loop over 10000 elements !
// just access them directly with a for loop !
// |
// V
for (i = 0; i < oldArr.length; i=i+delta) {
arr.push(oldArr[i]);
}
log('delta : ' + delta + ' length = ' + oldArr.length) ;
log(arr);
Filter itself returns an array. If I'm understanding you correctly, you don't need that surrounding loop. So:
newArr = oldArr.filter(function(value, index, Arr) {
return index % 3 == 0;
});
will set newArr to every third value in oldArr.
Try
arr = oldArr.filter(function (value, index, ar) {
return (index % ratio == 0);
} );
where ratio is 2 if you want arr to be 1/2 of oldArr, 3 if you want it to be 1/3 of oldArr and so on.
ratio = Math.ceil(oldArr.length / size); // size in the new `arr` size
You were calling filter() on each element of oldAdd inside a loop and you're supposed to call filter() on the whole array to get a new filtered array back.
Borrowing from #anonomyous0day's solution, generate a new Array with the desired indices from the given array:
(Take every 3 items)
Array.prototype.take = function(n) {
if (!Number(n) && n !== 0) {
throw new TypeError(`Array.take requires passing in a number. Passed in ${typeof n}`);
} else if (n <= 0) {
throw new RangeError(`Array.take requires a number greater than 0. Passed in ${n}`);
}
const selectedIndicesLength = Math.floor(this.length / n);
return [...Array(selectedIndicesLength)].map((item, index) => this[index * n + 1]);
};
[1, 2, 3, 4, 5, 6, 7, 8].take(2); // => 2, 4, 6, 8
this also works by using map to create the new array without iterating over all elements in the old array..
// create array with 10k entries
const oldArr = [ ...Array( 10000 ) ].map( ( _el, i ) => i );
const max = 10;
const delta = Math.floor( oldArr.length / max );
const newArr = [ ...Array( max ) ].map( ( _el, i ) => (
oldArr[ i * delta ]
) );
console.log( newArr );
may help!
const myFunction = (a, n) => {
let array = []
for(i = n; i <= a.length; i += n){
array.push(a[i-1]);
}
return array;
}

Categories