I was looking to create a binary search tree visualizer, like in visualalgo.net in which I generate 10 random elements in javascript and those elements get used as values for the nodes to create a binary search tree. My problem is I am not able to figure out the logic for correctly positioning the elements so that it looks like a bst. For example a value smaller than the root must be placed to the left, a little to the bottom as compared to the root, wheras a larger value must be placed to the right and so on using CSS
Using javascript I created 10 random elements and created as many division HTML elements, using Javascript's method of createElement(), as the number of random Elements. The divs innerText was set to the random values by iterating through a loop.
HTML:
<div class="elements"> </div>
JavaScript
let nodeElements = document.querySelector(".elements")
const randomArr = [];
for (let i = 0; i < 10; i++) {
let randomValue = Math.floor(Math.random() * 50);
randomArr.push(randomValue);
}
let newNode;
for (let i = 0; i < randomArr.length; i++) {
newNode = document.createElement("div");
newNode.classList.add("node"); //To style the division to make it look like a circular node
newNode.innerText = randomArr[i];
nodeElements.appendChild(newNode); //node Elements is a wrapper Division encapsulating all the nodes being created in javascript
}
My current output
Related
I'm learning Javascript and as an exercise I want to take a list of text ratings and convert it into star ratings.
There are probably some easier ways and libraries to do this but I wanted to try it with my own code just so I can understand how the code works.
I've mostly stumbled my way to a nearly working solution. However, I keep getting an extra star after going through the loops.
Here is a link to fiddle with the full code...
Fiddle
This is my HTML
<div class="container" id="container">
<div class="rating" id="rating">1.2</div>
<div class="rating" id="rating">3.2</div>
<div class="rating" id="rating">4.8</div>
<div class="rating" id="rating">5</div>
<div class="rating" id="rating">2.6</div>
</div>
this is my javascript
I'm basically getting all the ".ratings" elements from the parent "container"
const ratingsContainer = document.getElementById('container');
const ratings = ratingsContainer.getElementsByClassName('rating');
Iterating through them;
In the for loop Im fetching the innerHTML of each ratings div.
const ratingScore = parseFloat(ratings[i].innerHTML).toFixed(1);
Im converting them to decimal numbers because I have to split the rating in order to get the number of full stars needed and the number of fractional stars needed.
Im also calculating the number of empty stars needed.
let emptyStars;
emptyStars = fullStars < 5 ? 5 - fullStars - 1 : 0;
I then create the required elements for the fullstars, the fractional stars and the empty stars through a loop for each that looks something like this...
for (let i = 0; i < fullStars; i++) {
const newstarContainer = document.createElement('div');
newstarContainer.className = 'starContainer';
const starFill = document.createElement('div');
starFill.className = 'starFill';
const starFillImage = document.createElement('img');
starFillImage.src = 'images/star.svg';
const starOutline = document.createElement('div');
starOutline.className = 'starOutline';
const starOutlineImage = document.createElement('img');
starOutlineImage.src = 'images/outline.svg';
const newstarContainerFill = newstarContainer
.appendChild(starFill)
.appendChild(starFillImage);
const newstarContainerOutline = newstarContainer
.appendChild(starOutline)
.appendChild(starOutlineImage);
document
.getElementById('container')
.appendChild(newRatingsContainer)
.appendChild(newstarContainer);
}
I do this for the empty and fractional stars as well.
The fractional stars get a custom width style added to the star image container this cuts off the star.svg image based on the percentage width.
const starFill = document.createElement('div');
starFill.className = 'starFill';
starFill.style.width = `${fractionalStars}%`;
Finally I append everything to the container and remove the original text ratings.
My problem is when the text ratings number is 0 or not existing, I get an extra star. I've tried to play around with the code but I cant seem to figure out why its happening.
Can someone please help me to explain what Im doing wrong.
Got a few pointers and finally came up with something that works.
The first issue was getting an extra star.
That turned out to be a problem with the part of the code that fractional and empty stars.
I was running the loop like this...
for (let i = 0; i < 1; i++) {
That meant it always run at least once. This meant that when there was no fractional star to be added like in the case of an integer without a float, the loop still run and generated a star with a width of 0%. Which looked like extra empty star. I solved that by changing the loop to an if statement since it was only supposed to run once. So the code became
if (fractionalStarsFill > 0) {
The second problem was the way I was getting the number of empty stars required. I was doing it like this...
emptyStars = fullStars < 5 ? 5 - fullStars - 1 : 0;
Which is obviously wrong. I think I was tired.
I changed that to
emptyStars = fractionalStarsFill > 0 ? 4 - fullStars : 5 - fullStars;
This takes into account whether there are fractional stars or not and generates the number of empty stars accordingly.
Finally, I refactored the code and moved the repeated statements into a function, and changed a few variables to more meaningful names.
Here is a Fiddle to the final solution.
I've inherited the job of maintaining and developing an internal journaling system for registering inventory in tables on a local website. It is a website made in PHP, using jquery and handontable to list data from a MySQL database. All fields in the table are editable by the users.
Today the loading of data can be slow (10-15 seconds in the largest tables), which is mainly because of the loops used to populate the table and adjust the column sizes.
What do you think would be the best way to fix this issue? Should I reduce load times by fixing the loops, and keep handsontable as table library? Or should I scrap the old solution and implement something new?
Thanks :)
EDIT
I just saw you're using handsontable so my answer doesn't really provide a solution, as handsontable already uses a kind of list virtualization. I'll leave my answer anyway
Original Answer
What you can probably do is some sort of list virtualization, although this might be a bit tricky with table elements because you need absolute positioning and control of heights. Also it generally assumes that all rows have the same height.
The general idea is you only want to bother with rendering what's currently on the screen. Assuming you can fit 50 rows into your viewport at any time, you're measuring and updating 650 rows that don't matter. If you have 500000 rows, like in the fiddle, you're problem is going to be exponentially out of control.
Without knowing what you're doing exactly, here's a very general approach to the problem:
var elements = [];
var maxLength = 500000; // Number of elements we're going to generate
var itemHeight = 20; // We need a static row height for this to work
var totalHeight = itemHeight * maxLength; // The total height of the content
var $scrollContainer = $('#scroller-container'); // The container that will scroll
var $scrollContent = $('#scroller-contents'); // The content container for our items.
// We need to set the total height of the content so that absolute positioning works and the container receives the correctly sized scroll bar.
$scrollContent.css({ height: `${totalHeight}px` });
// Generate elements.
for (let i = 0; i < maxLength; i++) {
elements.push({
name: `item_${i}`,
value: `value_${i + 100}`
});
}
// By taking some measurements we will find out
// here exactly what items need to be rendered.
function obtainRenderableItems () {
// The size of our scrollable container
var containerHeight = $scrollContainer.height();
// How many items will fit inside the viewable area of our scrollable container
var viewport_count = Math.ceil(containerHeight / itemHeight);
// Where is it currently scrolled to.
var scrollPosition = $scrollContainer.scrollTop();
// The index of the first item in the viewable area
var start = Math.floor(scrollPosition / itemHeight);
// This calculation gives us a number of items to buffer on either side
// which prevents some janky behaviour when scrolling over yet unrendered items
var preScan = start - viewport_count <= 0 ? 0 : start - viewport_count;
// Basically we get the elements visible on the viewports by the current start
// index, and a buffer at the beginning and the end of the same amount of items
// in the viewport.
return elements.slice(preScan, preScan + (viewport_count * 3)).map((element, index) => {
return [preScan + index, element];
});
};
// Convert it to HTML, you can do whatever here, demo only.
function generateHTML (elements) {
return elements.map(el => {
let div = document.createElement('div');
div.className = 'element';
div.style.height = `${itemHeight}px`;
div.style.top = `${el[0] * itemHeight}px`;
div.innerHTML = `${el[1].name} - ${el[1].value}`;
return div.outerHTML;
}).join('');
}
// When we scroll we recalculate what items need to be shown and rerender them
// inside the page.
function onScroll (event) {
let items = obtainRenderableItems();
let htmlContent = generateHTML(items);
$scrollContent.html(htmlContent);
}
$scrollContainer.scroll(onScroll);
// Run at the beginning
onScroll();
The jQuery example above is based on a React component I wrote for exactly this purpose. You'll have to excuse my jQuery I haven't used it in years.
See the fiddle
There are a number of caveats with this approach. The major one being the row height must be the same for all rows, which is not workable for a number of situations. It also relies on a fixed container height, although the flex model can work around this.
I have a small web app that models deafness based on a person's individual audiogram (http://howdeaf.com). Essentially, it goes:
source -> 2-ch splitter -> 6 inline frequency separators -> merge node -> destination
-> 6 inline frequency separators ->
The frequency separators are created using this code:
for (let i = 0; i < bandSplit.length; i++) {
for (let j in sides) {
var side = sides[j];
let filterNode = context.createBiquadFilter();
filterNode.frequency.value = bandSplit[i];
if (i === 0)
filterNode.type = 'lowshelf';
else if (i === bandSplit.length - 1)
filterNode.type = 'highshelf';
else
filterNode.type = 'peaking';
filterNode.gain.value = 0.0;
this.eqNodes[side].push(filterNode);
if (i > 0)
this.eqNodes[side][i - 1].connect(this.eqNodes[side][i]);
}
}
I adjust the gain on each of the 12 frequency separator nodes, and it's a pretty neat simulation.
I'd like to add distortion (using createWaveShaper()), individually, using different curve values, to each of those 12 biquad filter nodes, but I've not had any success distorting just the individual frequency. Any distortion node I create affects the entire audio output for that channel.
Is there any way to apply distortion to those frequency-selection nodes on an individual basis?
All but two of your filters are peaking filters with a gain of 0. That basically lets everything through. So whatever wave shaping you apply gets applied to the whole frequency band. (See, for example, http://googlechrome.github.io/web-audio-samples/samples/audio/mag-phase.html to get graphs of the responses.)
Consider using different kinds of filters such as bandpass filters. This might not be appropriate for you application, but at least these filters don't let all frequencies through.
I have several JavaScript arrays, each containing a list of pointers to objects. When an object meets a certain condition, its pointer must be removed from its current containing array and placed into a different array.
My current (naive) solution is to splice out the exiting array elements and concatenate them onto the array they are entering. This is a slow method and seems to fragment memory over time.
Can anyone offer advice (general or JS-specific) on a better way to do this?
Demonstration code:
// Definitions
TestObject = function() {
this.shouldSwitch = function() {
return(Math.random() > 0.9);
}
}
A = [];
B = [];
while(A.length < 500) {
A.push(new TestObject());
}
// Transfer loop
doTransfers = function() {
var A_pending = [];
var B_pending = [];
for(var i = 0; i < A.length; i++) {
if(A[i].shouldSwitch()) {
B_pending.push(A[i]);
A.splice(i,1);
i--;
}
}
for(var i = 0; i < B.length; i++) {
if(B[i].shouldSwitch()) {
A_pending.push(B[i]);
B.splice(i,1);
i--;
}
}
A = A.concat(A_pending);
B = B.concat(B_pending);
}
setInterval(doTransfers,10);
Thanks!
For a language-independent kind of solution to this problem, when you're transferring elements from one contiguous sequence (array) to another, it's not appending elements to the back of the new array that's going to be bottlenecky (constant time complexity), it's going to be the removal of elements from the middle of your existing container (linear time complexity).
So the biggest benefit you can get is to replace that linear-time operation of removing from the middle of the array with a constant-time operation that still uses that cache-friendly, contiguous array representation.
One of the easiest ways to do this is to simply create two new arrays instead of one: a new array to append the elements you want to keep and a new array to append the elements you want to transfer. When you're done, you can swap out the new array of elements you want to keep (not transfer) with the old array you had.
In such a case, we're exchanging linear-time removals from the middle of a container with amortized constant-time insertions to the back of a new one. While insertion to the end of a container still has a worst-case complexity of O(N) for reallocations, it occurs infrequently enough and is still generally far better than paying for an operation that has an average complexity of O(N) every time you transfer a single element by constantly removing from the middle.
Another way to solve this problem that can be even more efficient, especially for certain cases like really small arrays since it only creates 1 new array, is this:
... when you transfer an element, first append a copy of it (possibly just a shallow copy) to the new container. Then overwrite the element at that index in the old container with the element from the back of the old container. Now simply pop off the element at the back of the old container. So we have one push, one assignment, and one pop.
In this case, we're exchanging a linear-time removal from the middle of a container with a single assignment (store/move instruction) and a constant-time pop from the back of the container (often basic arithmetic). This can work extremely well if the order of the elements in the old array can be shuffled around a little bit, and is often an overlooked solution for getting that linear-time removal from the middle of the array into one with constant-time complexity from the back of the array.
splice is pretty harmful for performance in a loop. But you don't seem to need mutations on the input arrays anyway - you are creating new ones and overwrite the previous values.
Just do
function doTransfers() {
var A_pending = [];
var B2_pending = [];
for (var i = 0; i < A.length; i++) {
if (A[i].shouldSwitch())
B_pending.push(A[i]);
else
A_pending.push(A[i]);
}
var B1_pending = [];
for (var i = 0; i < B.length; i++) {
if (B[i].shouldSwitch())
A_pending.push(B[i]);
else
B1_pending.push(B[i]);
}
A = A_pending;
B = B1_pending.concat(B2_pending);
}
I want to represent a board game in javascript and for that i need a bidimensional array.
I have different board sizes, so i need to initialize the array with the board size in the beginning. I am a java programmer so i know that in java when you want a bidimensional array you just do:
int board_size = 6;
String board = new String[board_size][board_size];
How can i achieve this with javascript? I have searched and found some ways of doing this, but all much less intuitive than this one.
It is not required like in Java or C#. The Javascript arrays grow dynamically, and they are optimized to work that way, so you don't have to set the size of your matrix dimensions upfront.
However, if you insist, you could do something like the following to pre-set the dimensions:
var board = [];
var board_size = 6;
for (var i = 0; i < board_size; i++) {
board[i] = new Array(board_size);
}
So to summarize you just have three options:
Initialization with a literal (like in #Geohut answer)
Initialization with a loop (like in my example)
Do not initialize upfront, but on-demand, closer to the code that access the dimensions.
With JavaScript it is not a static language like Java. It is dynamic. That means you can create the array without a need to preset the size of the array, but if you want you can procreate an array of the size you want.
var items = [[1,2],[3,4],[5,6]];
alert(items[0][0]); // 1
If you need to add to it just do
items.push([7,8]);
That will add the next element.
Code taken from old stack overflow post: How can I create a two dimensional array in JavaScript?
Edited to properly make I in items same as variable declaration.