how to shuffle an array except for the item in the middle? - reactjs

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

Related

I have an array of n number of observable, and i want to make 4 request at a time then again 4, till the end

how do i limit parallel request?
...
for (let offset = 0; offset < file[0].size; offset += chunkSize) {
const chunk = file[0].slice(offset, offset + chunkSize);
const blockId = String(Math.floor(100000 + Math.random() * 900000));
const base64BlockId = btoa(blockId);
this.blockids.push(base64BlockId);
request.push(this.blobService.addNewVideo(uniqueName, chunk, base64BlockId));
}
forkJoin(
request.map(req => req.pipe(
tap((event: HttpEvent<any>) => {
switch (event.type) {
case HttpEventType.UploadProgress:
this.progress = Math.round((event.loaded / event.total!) * 100);
// console.log(`uploaded! ${this.progress}%`);
break;
case HttpEventType.Response:
console.log('Successfully uploaded', event.body," : ",event.status,event.statusText);
}
})
))
).subscribe(() => {
this.commit(uniqueName);
});
...
You can use mergeAll for concurrent requests.
Ref: https://levelup.gitconnected.com/limit-concurrency-with-rxjs-and-promises-78590d2c85d0

Sort an array of date ranges in typeScript

Array(6) [
"10-2022 - 12-2022",
"08-2022 - 09-2022",
"07-2023 - 10-2023",
"04-2022 - 07-2022",
"01-2023 - 06-2023",
"01-2022 - 03-2022" ]
I want to sort this array of date ranges to show the latest date range at the beginning of the array. Having some trouble because the dates are in string format.
Try with this utility function:
const arr = [
"10-2022 - 12-2022",
"08-2022 - 09-2022",
"07-2023 - 10-2023",
"07-2023 - 11-2023",
"04-2022 - 07-2022",
"01-2023 - 06-2023",
"01-2022 - 03-2022"
];
const getYearMonth = (date) => {
const dateSplit = date.split('-');
if (dateSplit.length < 2) return '';
return dateSplit[1] + '-' + dateSplit[0];
}
const sortedArr = arr.sort((a, b) => {
const aSplit = a.split(' - ');
const bSplit = b.split(' - ');
const aYearMonthStart = getYearMonth(aSplit[0]);
const bYearMonthStart = getYearMonth(bSplit[0]);
// Sort decreasing by start
if (aYearMonthStart > bYearMonthStart) return -1;
if (aYearMonthStart < bYearMonthStart) return 1;
// Sort decreasing by end date if start date equal
const aYearMonthEnd = getYearMonth(aSplit[1]);
const bYearMonthEnd = getYearMonth(bSplit[1]);
if (aYearMonthEnd > bYearMonthEnd) return -1;
if (aYearMonthEnd < bYearMonthEnd) return 1;
// Equal dates
return 0;
})
console.log(sortedArr);
Array.sort((a, b) => b.localeCompare(a))

How do you create an array of meshes in scene in THREE.JS?

I'm trying to create an array of meshes I added to my THREE.JS scene because I used a for-loop with the load() function to create the instances of the meshes, and I wanted to reference them later for the animate() function. The array shows a list of values in the console, but the length shows up as 0 when logged. This is the code I have:
const manager = new THREE.LoadingManager(); //loading manager
const loader = new GLTFLoader(manager);
let pos_arr = []
manager.onLoad = function(e) {
scene.traverse( function (model) {
if (model.isMesh) {
pos_arr.push(model)
}
})
}
let pos = new THREE.Vector3()
//spheres and equators
let ball = new THREE.Mesh();
let equator = new THREE.Mesh()
for (let i = 1; i <= 12; i++) {
let scale = Math.floor(Math.random() * 3) + 1
let pos_x = Math.floor(Math.random() * 10) * 5 - 25
let pos_y = Math.floor(Math.random() * 10) * 5 - 25
let pos_z = Math.floor(Math.random() * 10) * 5 - 25
pos = new THREE.Vector3(pos_x, pos_y, pos_z)
loader.load( './ball.gltf', function ( gltf ) { //load sphere
gltf.scene.traverse(function(model) {
if (model.isMesh) {
model.castShadow = true;
model.material = sphereMaterial;
}
});
ball = gltf.scene
ball.position.set(pos_x, pos_y, pos_z)
ball.scale.set(scale, scale, scale)
scene.add( ball );
}, undefined, function ( error ) {
console.error( error );
} );
loader.load( './equator.gltf', function ( gltf2 ) {
gltf2.scene.traverse(function(model) { //for gltf shadows!
if (model.isMesh) {
model.castShadow = true
model.material = equatorMaterial
}
});
equator = gltf2.scene
equator.position.set(pos_x, pos_y, pos_z)
equator.scale.set(scale, scale, scale)
scene.add( equator )
}, undefined, function ( error ) {
console.error( error )
} );
}
//light
const light = new THREE.AmbientLight( 0xffffff );
scene.add( light );
console.log(pos_arr)
console.log(pos_arr.length)
This is what the console shows:
I got a recommendation that I use the onLoad attribute for the LoadingManager, which I used, but it didn't work. If anyone happens to know a possible solution, it would be greatly appreciated :)

Why my array getting appended instead of clearing and Adding new data

I am trying to achieve a method in which the array steps got filled with new data every time I click on the button of Create New array, but instead of that, the data is getting appended instead of updating.
here are my states :
const [arr , setArray] = useState(createArray())
const [steps , setSteps] = useState([]);
const [selectedAlgorithm , setSelectedAlgorithm] = useState ();
here is my create new Array function :
const handleCreateNewData = ()=>{
let newarr = createArray();
setArray ([]);
setArray([...newarr]);
setSteps ([]);
setTimeout(()=>{
if ( algorithms[selectedAlgorithm] !== undefined){
algorithms[selectedAlgorithm](arr, steps , setSteps);
console.log('running')
}
},2000)
}
here is my bubble sort algorithm :
export const BubbleSort = (array , steps ,setSteps) =>{
let funarray = new Array();
funarray = [...array] ;
for (let i = 0 ; i < funarray.length-1 ; i++){
for(let j = 0 ; j < funarray.length-1 ; j++){
if(funarray[j]>funarray[j+1]){
[funarray[j],funarray[j+1]] = [funarray[j+1],funarray[j]]
setSteps([...steps, funarray])
steps.push(funarray.slice());
console.log('Working')
}
}
}
return funarray;
}
What is supposed to do is every time I click on create new array it should generate a new set of arrays but instead of creating new arrays it just appending the new arrays in the old steps.
You can create a temp array to hold the steps, then when the loops are done, call setSteps:
const BubbleSort = (array, steps, setSteps) => {
let funarray = [];
funarray = [...array];
let temp = [];
for (let i = 0; i < funarray.length - 1; i++) {
for (let j = 0; j < funarray.length - 1; j++) {
if (funarray[j] > funarray[j + 1]) {
[funarray[j], funarray[j + 1]] = [funarray[j + 1], funarray[j]];
temp.push(funarray)
}
}
}
setSteps(temp);
return funarray;
};
Sample: https://codesandbox.io/s/cool-wind-ijj7z?file=/src/App.js

PointCloud Component render issue fetch() custom data [react-three-fiber]

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.

Resources