I am trying to make sorting visualizer using react, I also made algorithms ready for different sort mechanism like merge sort, bubble sort, insertion sort and Quick sort but not able to implement it visually.
I made visualizer for bubble sort but for that too I asked question in stack overflow and one of the guys gave the entire logic of it using react, now I am trying to make it possible for merge sort, spend 4 days to implement it but still I got stuck, it feels like I will not able survive without taking any help.
Can anyone please suggest me what to do?
my App.js file
import "./styles.css";
import { useState, useEffect } from "react";
export default function App() {
const [arrayForSort, setArrayForSort] = useState([
70,
20,
40,
90,
26,
10,
80,
50
]);
const [isSort, setIsSort] = useState(false);
const fetchArray = arrayForSort.map((element) => {
return (
<span className="array-element" key={element}>
{element}
</span>
);
});
const beginSort = () => {
setIsSort(true);
};
const mergeSort = (myArray) => {
let newArray = [...myArray];
// if array contains nothing or only one element then return the array
if (myArray.length <= 1) return myArray;
// find the middle of the array
const middle = Math.floor(myArray.length / 2);
// Split the array into two
const leftArray = myArray.slice(0, middle);
const rightArray = myArray.slice(middle);
// recursive function to combine left and right array
return merge(mergeSort(leftArray), mergeSort(rightArray), newArray);
};
const merge = (leftArr, rightArr, originalArray) => {
let resultArray = [],
leftIndex = 0,
rightIndex = 0;
while (leftIndex < leftArr.length && rightIndex < rightArr.length) {
if (leftArr[leftIndex] < rightArr[rightIndex]) {
let newLeftIndex = originalArray.indexOf(leftArr[leftIndex]);
let newRightIndex = originalArray.indexOf(rightArr[rightIndex]);
[originalArray[newLeftIndex], originalArray[newRightIndex]] = [
originalArray[newRightIndex],
originalArray[newLeftIndex]
];
resultArray.push(leftArr[leftIndex]);
leftIndex++;
} else {
let newLeftIndex = originalArray.indexOf(leftArr[leftIndex]);
let newRightIndex = originalArray.indexOf(rightArr[rightIndex]);
[originalArray[newRightIndex], originalArray[newLeftIndex]] = [
originalArray[newLeftIndex],
originalArray[newRightIndex]
];
resultArray.push(rightArr[rightIndex]);
rightIndex++;
}
}
setArrayForSort(originalArray);
resultArray = resultArray
.concat(leftArr.slice(leftIndex))
.concat(rightArr.slice(rightIndex));
return resultArray;
};
useEffect(() => {
if (isSort) {
const arr = [...arrayForSort];
const sortedArray = mergeSort(arr);
console.log(sortedArray);
// setArrayForSort(sortedArray);
setIsSort(false);
}
}, [arrayForSort, setArrayForSort, isSort, mergeSort]);
return (
<div className="App">
<h1>Merge Sort</h1>
<div className="array">{fetchArray}</div>
<button onClick={beginSort}>SORT</button>
</div>
);
}
This is actually trickier than I originally thought it was going to be. I wanted each individual "swap" the merge sort detected to be displayed, but I'm having problems going "back up the splits" correctly with my original idea.
I'll probably revisit this, but in case I don't, here's a version that just slightly tweaks your original code. You'll notice the bigger splits just sort of "snap" into being in the correct order, which probably isn't what you want, but hopefully it helps with understanding Timeouts and Promises:
https://codesandbox.io/s/merge-sort-forked-coh7r?file=/src/App.js
Just to be clear, everything is still basically happening in "one shot", meaning the call stack is still pretty much identical, meaning all the functions are still being called right on top of each other. The difference is that the Promise/setTimeout is adding a "pause here then run this" and the async/await is adding a "wait for that before continuing". I think if you really wanted to show off "each swap" you would have to really break up the control flow and add in some conditional steps.
Related
I have thought about this alot but i cant find a good solution..that is also fast in Javascript.
I have an array of objects..the objects are game searches for a random player.
The array may look like this:
const GameSearches[
{fromPlayerId:"378329",goalScore:20}
{fromPlayerId:"125342",goalScore:20}
{fromPlayerId:"378329",goalScore:20}
{fromPlayerId:"918273",goalScore:20}
{fromPlayerId:"378329",goalScore:20}
]
In this array i need to rund a function called CreateNewGame(Player1,Player2).
In this array i could create games with for example index 0 and 1. Index 2 and 3. Index 4 would be left in the array as there are no more players to match on.
Anyone got a good solution to this? It would really help me out.
I have tried different filter and map without finding a good solution.
The output should call a function createnewgame
Example:
createNewGame(GameSearches[0].from,GameSearches[1].from)
this function will be called as there are two players looking for a game. they do not have they same fromPlayerId so they should match.
I see some comments on that StackOverflow is not a free codingservice..the app has thousands of lines..this is only a small part. Im asking becouse i cant figure out the logic on how to to this. I do not need a full working example.
You can try something like this
const GameSearches = [
{fromPlayerId:"378329",goalScore:20},
{fromPlayerId:"125342",goalScore:20},
{fromPlayerId:"378329",goalScore:20},
{fromPlayerId:"918273",goalScore:20},
{fromPlayerId:"378329",goalScore:20}
];
const createNewGames = (player1, player2) => console.log(player1.fromPlayerId, player2.fromPlayerId)
const getMatch = (GameSearches) => {
while([...new Set(GameSearches)].length > 1){
const player1 = GameSearches[0];
GameSearches.shift();
const player2Index = GameSearches.findIndex(el => el.fromPlayerId !== player1.fromPlayerId)
const player2 = GameSearches[player2Index];
GameSearches.splice(player2Index,1)
createNewGames(player1, player2);
}
}
getMatch(GameSearches);
I think maybe i can use the suggestion of a for loop..and it will work fine.
for (let i = 0; i < games20GoalScore.length; i = i + 2) {
if (games20GoalScore[i + 1] !== undefined) {
console.log(games20GoalScore[i] + " : " + games20GoalScore[i + 1]);
if (games20GoalScore[i].from !== games20GoalScore[i + 1].from) {
console.log("Match");
}
}
}
This code is run each time the array get a new item.
I am attempting to iterate over a very large 2D array in JavaScript within an ionic application, but it is majorly bogging down my app.
A little background, I created custom search component with StencilJS that provides suggestions upon keyup. You feed the component with an array of strings (search suggestions). Each individual string is tokenized word by word and split into an array and lowercase
For example, "Red-Winged Blackbird" becomes
['red','winged','blackbird']
So, tokenizing an array of strings looks like this:
[['red','winged','blackbird'],['bald','eagle'], ...]
Now, I have 10,000+ of these smaller arrays within one large array.
Then, I tokenize the search terms the user inputs upon each keyup.
Afterwards, I am comparing each tokenized search term array to each tokenized suggestion array within the larger array.
Therefore, I have 2 nested for-of loops.
In addition, I am using Levenshtein distance to compare each search term to each element of each suggestion array.
I had a couple drinks so please be patient while i stumble through this.
To start id do something like a reverse index (not very informative). Its pretty close to what you are already doing but with a couple extra steps.
First go through all your results and tokenize, stem, remove stops words, decap, coalesce, ects. It looks like you've already done this but i'm adding an example for completion.
const tokenize = (string) => {
const tokens = string
.split(' ') // just split on words, but maybe rep
.filter((v) => v.trim() !== '');
return new Set(tokens);
};
Next what we want to do is generate a map that takes a word as an key and returns us a list of document indexes the word appears in.
const documents = ['12312 taco', 'taco mmm'];
const index = {
'12312': [0],
'taco': [0, 1],
'mmm': [2]
};
I think you can see where this is taking us... We can tokenize our search term and find all documents each token belongs, to work some ranking magic, take top 5, blah blah blah, and have our results. This is typically the way google and other search giants do their searches. They spend a ton of time in precomputation so that their search engines can slice down candidates by orders of magnitude and work their magic.
Below is an example snippet. This needs a ton of work(please remember, ive been drinking) but its running through a million records in >.3ms. Im cheating a bit by generate 2 letter words and phrases, only so that i can demonstrate queries that sometimes achieve collision. This really doesn't matter since the query time is on average propionate to the number of records. Please be aware that this solution gives you back records that contain all search terms. It doesn't care about context or whatever. You will have to figure out the ranking (if your care at this point) to achieve the results you want.
const tokenize = (string) => {
const tokens = string.split(' ')
.filter((v) => v.trim() !== '');
return new Set(tokens);
};
const ri = (documents) => {
const index = new Map();
for (let i = 0; i < documents.length; i++) {
const document = documents[i];
const tokens = tokenize(document);
for (let token of tokens) {
if (!index.has(token)) {
index.set(token, new Set());
}
index.get(token).add(i);
}
}
return index;
};
const intersect = (sets) => {
const [head, ...rest] = sets;
return rest.reduce((r, set) => {
return new Set([...r].filter((n) => set.has(n)))
}, new Set(head));
};
const search = (index, query) => {
const tokens = tokenize(query);
const canidates = [];
for (let token of tokens) {
const keys = index.get(token);
if (keys != null) {
canidates.push(keys);
}
}
return intersect(canidates);
}
const word = () => Math.random().toString(36).substring(2, 4);
const terms = Array.from({ length: 255 }, () => word());
const documents = Array.from({ length: 1000000 }, () => {
const sb = [];
for (let i = 0; i < 2; i++) {
sb.push(word());
}
return sb.join(' ');
});
const index = ri(documents);
const st = performance.now();
const query = 'bb iz';
const results = search(index, query);
const et = performance.now();
console.log(query, Array.from(results).slice(0, 10).map((i) => documents[i]));
console.log(et - st);
There are some improvements you can make if you want. Like... ranking! The whole purpose of this example is to show how we can cut down 1M results to maybe a hundred or so canidates. The search function has some post filtering via intersection which probably isn't what you want you want but at this point it doesn't really matter what you do since the results are so small.
"Use reduce and the hex2color function to count list of hex values than have r in their name";
My current attempt is below. The first piece I know needs to be fixed is the filter function. I need to be able to filter out any colors that have the letter "r", but cannot seem to find a way to easily fit that into the filter function. It could easily be a syntax issue as I think I am asking the filter to find any strings that === "r", even though I am trying to use "contains" to solve that and have it check the whole color word.
Once the filter function is working, I assume the next step is to simply use the reduce function, then compose them together. ( I could be way off off, however).
I am quite new to programming, any insight is extremely welcome. Thanks!!
const exercise3 = _ => {
const hexes = ["#0000ff", "#f5f5dc", "#cd853f", "#663399", "#ffa500"];
const letter = "r";
const mapper = hex2color;
console.log(map(mapper, hexes)); //blue,beige,peru,rebeccapurple,orange
const filterer = el => contains(hex2color(el), letter);
console.log(filter(filterer, hexes)); //yields nothing, I assume to using the filter wrong with "r".
const reducer = (acc, el) => acc + 1;
const mappedFn = map(mapper);
const filtererFn = filter(filterer);
const reducerFn = reduce(reducer, 0);
const composedFn = compose(reducerFn, filtererFn, mappedFn);
return composedFn(hexes);
};
Let me be the first to say that this isn't something I normally do, but out of curiousity, I'll see if anyone has a good idea on how to handle a problem like this.
The application I am working on is a simulated example of the game Let's make a Deal featuring the Monty Hall problem.
I won't go into details about my implementation, but it more or less allows a user to enter a number of how many games they want to simulate, and then if an option is toggled off, the player of those x games won't switch their choice, while if it is toggled on, they will switch their choice every single instance of the game.
My object generator looks like this:
const game = function(){
this[0] = null;
this[1] = null;
this[2] = null;
this.pick = Math.floor(Math.random() * 3);
this.correctpick = Math.floor(Math.random() * 3);
this[this.correctpick] = 1;
for (let i=0; i<3; i++){
if ((this[i] !== 1) && (i !== this.pick)){
this.eliminated = i;
break;
}
}
}
const games = arg => {
let ret = [];
for(let i=0; i<arg; i++){
ret.push(new game);
}
return ret;
}
This structure generates an array which i stringify later that looks like this:
[
{
"0": 1,
"1": null,
"2": null,
"pick": 2,
"correctpick": 0,
"eliminated": 1
},
{
"0": null,
"1": null,
"2": 1,
"pick": 2,
"correctpick": 2,
"eliminated": 0
}
]
As sloppy as the constructor for game looks, the reason is because I have refactored it into having as few function calls as possible, where now I'm literally only calling Math functions at the current time (I removed any helper functions that made the code easier to read, in opt for performance).
This app can be ran both in the browser and in node (cross platform), but I have clamped the arg a user can pass into the games function to 5 million. Any longer than that and the process (or window) freezes for longer than a few seconds, or even potentially crashes.
Is there anything else I can do to increase performance if a huge number is given by a user? Also, if you need more information, I will be happy to supply it!
Thanks!
The obvious performance optimisation would be not to create and store 5 million objects at all, relieving memory pressure. Instead you'd create the objects on the fly only when you need them and throw them away immediately after. I'm not sure what your app does, but it sounds like you want to re-use the same game instances when evaluating results with the different options. In that case, you need to store them of course - but I'd advise to re-think the design and consider immediately evaluating each game with all possible options, accumulating only the results for each choice of options but not keeping all games in memory.
Apart from that, I'd recommend to simplify a bit:
You can drop that loop completely and use some clever arithmetic to find the eliminated option: this.eliminated = this.pick == this.correctpick ? +!this.pick : 3 - this.pick - this.correctpick;. Or use a simple lookup table this.eliminated = [1, 2, 1, 2, 0, 0, 1, 0, 0][this.pick * 3 + this.correctpick].
I'd avoid changing the type of the array elements from null (reference) to 1 (number). Just keep them as integers and initialise your elements with 0 instead.
Don't store 6 properties in your object that are completely redundant. You only need 2 of them: pick and correctpick - everything else can be computed on the fly from them when you need it. Precomputing and storing it would only be advantageous if the computation was heavy and the result was used often. Neither of this is the case, but keeping a low memory footprint is important (However, don't expect much from this).
Not sure about your implementation, but do you really need an Array?
How about only using results (see snippet)?
If it's blocking the browser that worries you, maybe delegating the work to a web worker is the solution for that: see this jsFiddle for a web worker version of this snippet.
(() => {
document.querySelector("#doit")
.addEventListener("click", playMontyHall().handleRequest);
function playMontyHall() {
const result = document.querySelector("#result");
const timing = document.querySelector("#timing");
const nOfGames = document.querySelector("#nGames");
const switchDoors = document.querySelector("#switchyn");
// Create a game
const game = (doSwitch) => {
const doors = [0, 1, 2];
const pick = Math.floor(Math.random() * 3);
const correctPick = Math.floor(Math.random() * 3);
const eliminated = doors.filter(v => v !== pick && v !== correctPick)[0];
return {
correctpick: correctPick,
pick: doSwitch ? doors.filter(v => v !== pick && v !== eliminated)[0] : pick,
eliminated: eliminated,
};
};
const getWinner = game => ~~(game.correctpick === game.pick);
// Sum wins using a generator function
const winningGenerator = function* (doSwitch, n) {
let wins = 0;
while (n--) {
wins += getWinner(game(doSwitch));
yield wins;
}
};
// calculate the number of succeeded games
const calculateGames = (nGames, switchAlways) => {
const funNGames = winningGenerator(switchAlways, nGames);
let numberOfWins = 0;
while (nGames--) {
numberOfWins = funNGames.next().value;
}
return numberOfWins;
}
const cleanUp = playOut => {
result.textContent =
"Playing ... (it may last a few seconds)";
timing.textContent = "";
setTimeout(playOut, 0);
};
const report = results => {
timing.textContent = `This took ${
(performance.now() - results.startTime).toFixed(3)} milliseconds`;
result.innerHTML =
`<b>${!results.switchAlways ? "Never s" : "Always s"}witching doors</b>:
${results.winners} winners out of ${results.nGames} games
(${((results.winners/+results.nGames)*100).toFixed(2)}%)`;
};
// (public) handle button click
function clickHandle() {
cleanUp(() => {
const nGames = nOfGames.value || 5000000;
const switchAlways = switchDoors.checked;
report({
switchAlways: switchAlways,
startTime: performance.now(),
winners: calculateGames(nGames, switchAlways),
nGames: nGames
});
});
}
return {
handleRequest: clickHandle
};
}
})();
body {
margin: 2em;
font: normal 12px/15px verdana, arial;
}
#timing {
color: red;
}
<p><input type="number" id="nGames" value="5000000"> N of games</p>
<p><input type="checkbox" id="switchyn"> Always switch doors</p>
<p><button id="doit">Play</button>
<p id="result"></p>
<p id="timing"></p>
I have a array of about 18 000 elements. I'm creating a map application where I want to add the elements when the user zooms in to a certain level.
So when the user zooms in under 9 I loop tru the array looking for elements that is in the view.
However, it does take some time looping thru the elements, causing the map application lag each time the user zooms out and in of "level 9". Even if there are no elements to add or not, so the bottleneck is the looping I guess.
I've tried to solve it by asyncing it like:
function SearchElements(elementArr) {
var ret = new Promise(resolve => {
var arr = [];
for (var i in elementArr) {
var distanceFromCenter = getDistanceFromLatLonInKm(view.center.latitude, view.center.longitude, dynamicsEntities[i].pss_latitude, dynamicsEntities[i].pss_longitude);
var viewWidthInKm = getSceneWidthInKm(view, true);
if (distanceFromCenter > viewWidthInKm) continue;
arr.push(elementArr[i]);
}
resolve(arr);
});
return ret;
}
SearchElements(myElementsArray).Then(arr => {
// ...
});
But its still not async, this method hangs while the for loop runs.
Because you still have a tight loop that loops through all the elements in one loop, you'll always have the responsiveness issues
One way to tackle the issue is to works on chunks of the data
Note: I'm assuming elementArr is a javascript Array
function SearchElements(elementArr) {
var sliceLength = 100; // how many elements to work on at a time
var totalLength = elementArr.length;
var slices = ((totalLength + sliceLength - 1) / sliceLength) | 0; // integer
return Array.from({length:slices})
.reduce((promise, unused, outerIndex) =>
promise.then(results =>
Promise.resolve(elementArr.slice(outerIndex * sliceLength, sliceLength).map((item, innerIndex) => {
const i = outerIndex * sliceLength + innerIndex;
const distanceFromCenter = getDistanceFromLatLonInKm(view.center.latitude, view.center.longitude, dynamicsEntities[i].pss_latitude, dynamicsEntities[i].pss_longitude);
const viewWidthInKm = getSceneWidthInKm(view, true);
if (distanceFromCenter <= viewWidthInKm) {
return item; // this is like your `push`
}
// if distanceFromCenter > viewWidthInKm, return value will be `undefined`, filtered out later - this is like your `continue`
})).then(batch => results.concat(batch)) // concatenate to results
), Promise.resolve([]))
.then(results => results.filter(v => v !== undefined)); // filter out the "undefined"
}
use:
SearchElements(yourDataArray).then(results => {
// all results available here
});
My other suggestion in the comment was Web Workers (I originally called it worker threads, not sure where I got that term from) - I'm not familiar enough with Web Workers to offer a solution, however https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers and https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API should get you going
To be honest, I think this sort of heavy task would be better suited to Web Workers