in my React app, I call shuffle 2 times for 2 different card sets but shuffle always gives 2 card sets the exact same result, can someone help me fix it?
class PairThemUp extends React.Component{
constructor(props){
super(props);
this.state={
cards1:[],
cards2:[],
}
}
shuffleCards=()=>{
const cards=this.props.selectedCards
const cards1=shuffle(cards)
const cards2=shuffle(cards)
this.setState({cards1, cards2})
const id1=cards1.map(c=>c.id)
const id2=cards2.map(c=>c.id)
console.log(id1, id2)
}
shuffle gives 2 card sets the same result until I run shuffleCards function again. this is my shuffle function
export const shuffle=(a)=> {
for (let i = a.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[a[i], a[j]] = [a[j], a[i]];
}
return a;
}
cards, cards1, and cards2 are all pointing to the same array in your example as JavaScript passes array by reference.
The result is that every time you call shuffle, you are modifying and returning the underlying array that was passed to the function, and therefore any variable that was pointing to a result from previously calling shuffle will reflect the most recently shuffled array.
The fix is to create a copy of your array in shuffle so that cards, cards1, and cards2 all point to different arrays:
let shuffle = (a) => {
let newArr = [].concat(a); // create new array
for (let i = a.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[newArr[i], newArr[j]] = [newArr[j], newArr[i]];
}
return newArr;
};
Related
Iยดm creating a Bingo board and I need that the one in the middle always stays the same even when shuffleing this array:
const bbb = [
"๐",
"๐",
"๐คฃ",
"๐",
"๐",
"๐
",
"๐",
"๐",
"๐",
"๐",
"๐ ",
"๐คฉ",
"๐ฏ",
"๐ถ",
"๐ซ",
"๐ด",
"๐ค ",
"๐ ",
"๐",
"๐ฏ",
"๐",
"๐ฅ",
"๐ฎ ",
"๐",
"๐"
];
const data = arrayShuffle(bbb).reduce(
(data, value, index) => ({ ...data, [index]: value }),
{}
);
and then Im maping the array to display the Tiles and create the board like this:
{Object.keys(data).map(id => (
<Tile
key={id}
id={id}
isSet={state.checked[id]}
onToggle={() => toggle(id)}
>
{data[id]}
</Tile>
))}
Remove the middle item from the array initially. Then do the in-place randomizing of items and finally attach the middle item to the array.
This runs in O(n) time complexity where n is the size of your array and you always get a uniform random permutation.
const bbb = [ "๐", "๐", "๐คฃ", "๐", "๐", "๐
", "๐", "๐", "๐", "๐", "๐", "๐คฉ", "๐ฏ", "๐ถ", "๐ซ", "๐ด", "๐ค", "๐", "๐", "๐ฏ", "๐", "๐ฅ", "๐ฎ", "๐", "๐", ];
const getRandomInt = (min, max) => {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min;
};
const arrayShuffleInplaceExceptMiddle = (A) => {
const middle = A.splice(A.length/2, 1);
const n = A.length;
const middleIndex = Math.floor(n / 2);
for (let i = 0; i < n; i++) {
let swapIndex = getRandomInt(i, n);
let a = A[i];
A[i] = A[swapIndex];
A[swapIndex] = a;
}
A.splice(n/2, 0, ...middle)
};
// test runs
Array.from({length: 10}, () => {
arrayShuffleInplaceExceptMiddle(bbb);
console.log(bbb.join(""));
})
Just shuffle the array normally, but remove the the value before the shuffle and insert it back afterward:
/**
* Durstenfeld shuffle
*
* - https://stackoverflow.com/a/12646864/438273
* - https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm
*
* #param {unknown[]} array
*/
function shuffleArray (array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
}
/**
* Like a normal shuffle, but for a bingo board
*
* #param {unknown[]} array
*/
function bingoShuffle (array) {
const index = Math.floor((array.length - 1) / 2);
const [value] = array.splice(index, 1);
shuffleArray(array);
array.splice(index, 0, value);
}
// Let's keep the board small for this demo:
const board = [
"๐",
"๐",
"๐",
"๐คฉ",
"๐ฏ",
"๐ถ",
"๐ซ",
"๐ด",
"๐ค",
];
console.log(board.join(' '));
// Shuffle it a few times and look at the results:
for (let i = 0; i < 10; i += 1) {
bingoShuffle(board);
console.log(board.join(' '));
}
And because you tagged this with reactjs, I'm guessing this is (immutable) state, so you'll need to get a new array when shuffling, like this:
const updatedBoard = bingoShuffle([...board]);
// ^^^^^^^^^^
// Shallow copy into new array so you don't mutate React state
I am trying to visualize a sorting algorithm with react, my algorithm works fine but i want to link it to divs on the DOM that change their height as the sorting algorithms sorts a given array, i have only made it work at the last stage, for example divs on the DOM take their height from an unsorted array and when i click on the sort button, instead of dynamicly changing their height as the algorithm sorts the unsorted array, they just wait untill the algorithm finishes sorting and then in one instance the div go from unsorted height to sorted height (i have tried to do setTimeout but it never worked for me as it intended, it just does one timeout instead of doing it in every iteration).
This is my code:
function App() {
const [array, setArray] = useState();
const elements = useRef();
const test = (Array) => {
if(Array){
for(let i=0; i < Array.length; i++){
elements.current.children[i].style.height = `${Array[i]*2}vh`;
}
}
};
useEffect(()=> {
const startArray = [];
for(let i=0; i < 51; i++){
let randomNumber = Math.floor(Math.random() * 27);
if(randomNumber !== 0){
startArray.push(randomNumber)
}
}
setArray(startArray)
}, [])
const bubbleSort = arraY => {
let iteration = true;
let shouldContinue = false;
let count = arraY.length-1;
while(iteration){
shouldContinue = false
if(count > 0){
for(let i=0; i < count; i++){
shouldContinue = true;
if(arraY[i] > arraY[i+1]){
let smaller = arraY[i+1];
arraY[i+1] = arraY[i];
arraY[i] = smaller;
test(arraY);
}
}
}
count = count -1
if(!shouldContinue){
iteration = false
}
}
return array
}
const sort = ()=> {
bubbleSort(array);
}
return (
<div className="App">
<nav>
<h1 onClick={sort}>BUBBLE SORT</h1>
<h1 onClick={sort}>--- SORT</h1>
<h1 onClick={sort}>--- SORT</h1>
<h1 onClick={sort}>--- SORT</h1>
</nav>
<div className='container' ref={elements}>
{array && array.map(item=>{
return <div style={{height: `${item*2}vh`}}></div>
})
}
</div>
</div>
);
}
Any help is appreciated
You need to do one sort operation, and then set the state, so that react re-renders. You also will need to set some timeout, to avoid it all happening so quickly you can't see anything. Something like this:
const [currentlyAt, setCurrentlyAt] = useState(0);
const bubbleSort () => {
const newArray = [...]
// perform one step of bubble sort, storing my position in the sort
// with setCurrentlyAt, so I can resume it next time I'm called
setArray(newArray);
setTimeout(() => bubbleSort(), 500);
}
This allows one step (a single item swap in bubble array) to be performed, state updates so it'll rerender, and timeout gives time for the rerender to be seen by the user before resuming the bubble sort and doing the next swap.
Also, please, don't use arraY and Array as variable names to avoid shadowing array, use something like partiallySorted or at least arr. And you don't need to useRef at all, your logic where you map over array and have div heights based on array value works fine, you don't want to manually edit the style using a ref.
Based on a snippet of original r3f-example found in PointCloud.js
Tested by myself, this above original component is able to render pointcloud by pushing individual x y z value into the for-loop in Particle() function.
I modified it and added a `fetch()' method to retrieve a custom data txt file, snippet as shown below,
...
export function Particles() {
const [positions, colors] = useMemo(() => {
let positions = [], colors = []
positions.length = 3
colors.length = 3
const HEADER_SIZE = 4;
let stream, longArray, len;
let clusterCount ;
let xy_size ;
let clusterSize = [];
let XY_arr = [];
fetch(map)
.then((r) => r.text())
.then(text => {
stream = text.toString().split("\n"); // split by next line
longArray = stream.slice(2,); // remove header from main longArray
len = longArray.length;
for (let i = 0, count = 0; i < len; i += HEADER_SIZE ) {
xy_size = longArray.slice((i + HEADER_SIZE - 1), (i + HEADER_SIZE));
XY_arr.push(longArray.slice((i + HEADER_SIZE ), (i + HEADER_SIZE + xy_size*2)));
console.log(" Points in PointCloud " + count + ": " + xy_size );
clusterSize.push(xy_size);
clusterCount = count;
i += xy_size*2;
count ++;
}
for (let i = 0; i < (clusterCount-2); i++) {
for (let j = 0; j < clusterSize[i]*2; j+=2) {
positions.push( XY_arr[i][j] )
positions.push(0)
positions.push( XY_arr[i][j+1] )
colors.push(1)
colors.push(0.5)
colors.push(0.5)
console.log( XY_arr[i][j] );
}
}
}
)
return [new Float32Array(positions), new Float32Array(colors)]
}, [])
...
...
, map is the custom text file in string, with single data line-by-line
The fetch() method is able to read a custom pointcloud file into XY_arr as an object of Array(). I have checked that XY_arr[i][j] in the nested-forloop are able to return correct x and z value in console.
Current problem is that no pointcloud being rendered onto <Canvas />
Is the problem caused by position.push() nested loop being inside of 'fetch()' method ? And how to resolve. Thank you.
better use const [state, set] = useState() and then fetch in useEffect calling "set" when you're done. putting an async fetch request inside useMemo is practically a side-effect in the render function - which isn't good, nor will it work like that.
Ok, so I am currently trying to add items from an object together prices to be exact. When I try to actually parse the items with parseInt,I keep getting NaN.
I have tried to replicate it on repl.it and it works fine, also I have tried to use Number instead of parseInt and same results.
class Cart extends Component {
constructor(props) {
super(props);
this.add = this.add.bind(this);
}
componentDidMount() {
this.props.getCart();
}
add(array) {
let num = [];
let parse = [];
console.log(array);
for (let i = 0; i < array.length; i++) {
parse.push(array[i].price);
}
console.log(parse);
for (let i = 0; i < parse.length; i++) {
num.push(parseInt(parse[i]));
}
console.log(num);
let sum = num.reduce((acc, val) => {
return acc + val;
}, 0);
console.log(sum);
}
render() {
const { cart } = this.props.itemReducer;
this.add(cart);
I do have a few console.log's
first one does show my obj's
the second show's that I pulled my numbers in the variable["$379.99", "$1,499.99"]
the third one where I actually do the parseInt function is when i get [NaN, NaN]`
There is a $ sign in your string. Remove the sign and try
let nanNum = parseInt("$1477")
console.log(nanNum) //This will print NaN
let parsedNum = parseInt("$147,7.05".replace(/[^0-9\.]+/g,""))
console.log(parsedNum)
This is because of ["$379.99", "$1,499.99"], here items in array contains $ which is not a number (NaN) causing the error.
Instead of this,
num.push(parseInt(parse[i]));
You can do this,
num.push(Number(parse[i].replace(/[^0-9.]/g, "")));
Demo
I am learning React and Javascript, I am writing a small app that have 40 squares with different colors and every second 2 colors will randomly change, everything works fine but I have one minor problem. How can I change my code so that if two numbers are the same one of them will be regenerated? I tried to use if like this
import React from 'react';
import ReactDom from 'react-dom';
const totalColor=40;
const Box=(props)=>{...}
class RandomColorApp extends React.Component{
constructor(props){
super(props);
const boxes = new Array(totalColor).fill().map(this.randomColor,this);
this.state={boxes}
setInterval(()=>{
const randomIndex1=Math.floor(Math.random()*3)
const randomIndex2=Math.floor(Math.random()*3)
if(randomIndex1!==randomIndex2){
console.log(randomIndex1,randomIndex2)
}
},1000);
}
randomColor(){...}
render(){...}
}
RandomColorApp.defaultProps={colors:[...]}
ReactDom.render(<RandomColorApp/>,document.getElementById('app'));
but the problem is, the whole process will be delayed by 1 second before it regenerates new numbers, and also is there a way I can refactor the code so I do not have to repeat Math.floor(Math.random()*3) too much in case I need more than 2 random numbers, thank you verymuch
below is my full code just in case it can help
import React from 'react';
import ReactDom from 'react-dom';
const totalColor=4;
const Box=(props)=>{
const style={
width: '180px',
height: '180px',
display: 'inline-block',
backgroundColor: props.color
}
return <div style={style}/>
}
class RandomColorApp extends React.Component{
constructor(props){
super(props);
const boxes = new Array(totalColor).fill().map(this.randomColor,this);
this.state={boxes}
setInterval(()=>{
const boxes=this.state.boxes.slice();
const randomIndex1= Math.floor(Math.random()*boxes.length);
const randomIndex2= Math.floor(Math.random()*boxes.length);
boxes[randomIndex1]=this.randomColor();
boxes[randomIndex2]=this.randomColor();
this.setState({boxes});
//const randomIndex1=Math.floor(Math.random()*3)
//const randomIndex2=Math.floor(Math.random()*3)
//if(randomIndex1!==randomIndex2){
// console.log(randomIndex1,randomIndex2)
//}
},1000);
}
randomColor(){
let colorIndex=Math.floor(Math.random()*this.props.colors.length)
return this.props.colors[colorIndex];
}
render(){
const boxes=this.state.boxes.map((color,index)=>(
<Box key={index} color={color}/>
))
return(
<div className='RandomColorApp'>
{boxes}
</div>
);
}
}
RandomColorApp.defaultProps={...}
ReactDom.render(<RandomColorApp/>,document.getElementById('app'));
First define below function to generate a random number in the given range which is not equal to the except value.
randomNumber = (max, min, except) => {
let num = Math.floor(Math.random() * (max - min +1)) + min;
return (num === except) ? randomNumber(max, min, except) : num;
}
After that use below code in your setInterval() method
const randomIndex1 = randomNumber(0, totalColor, -1);
const randomIndex2 = randomNumber(0, totalColor, randomIndex1)
In this case, randomIndex1 will never be equal to randomIndex2
To get a list of distinct numbers, you can use an array and shuffle it:
a=new Array(10).fill(1);
a=a.map((x,i)=>i);
a=a.sort(()=>{return 2*Math.random()-1});
for (let i=0;i<a.length;i++) console.log(a[i]);
Ok, this function will take a rangeMin, rangeMax, and selectionCount, and return an array containing selectionCount unique values from the range. It will not validate that Min != max, etc. This is efficient for non-large values of (rangeMax - rangeMin), and is much more efficient than random selection and testing when selectionCount is a non-trivial portion of (rangeMax - rangeMin).
var getSelections = function( rangeMin, rangeMax, selectionCount ) {
// make array containing all values possible
var arr=Array.from({length: (rangeMax-rangeMin)+1 }, (v, k) => k+rangeMin);
arr=arr.sort(()=>{return 2*Math.random()-1}); // shuffle the array
return arr.slice( 0, selectionCount );
}
var values = getSelections( 1, 40, 2 );
console.log( "2 values from 1-40" );
console.log( values );
var morevalues = getSelections( 1, 8, 5 );
console.log( "5 values from 1-8" );
console.log( morevalues );
//declare an array to add the numbers
let myArr = [];
//loop through the array to add up to 6 numbers ()
while (myArr.length < 6){
//assign random number to a variable
let random = Math.floor(Math.random()* 25) + 1;
//check if the number assigned to the variable exist in myArr[]
if(myArr.indexOf(random) > -1) {
console.log('Matching number ' + random + ', getting a new one');
//if number exist don't push the number and go back to the head of while loop
continue;
}
//if number doesn't exist push it to end of myArr[]
myArr.push(random);
}
console.log(myArr);