So I wrote a pretty basic code. I'm a real noob. Started JavaScript 3 days back. I want the sorting process to be visualized while the sorting is going on. But when the SORT button is clicked after a while the sorted array is show. But what I want is to show the changes happening to the array in real-time.
Please help me out.
var array = [];
container_content= "";
for (var i=0; i < 50; i++) {
array.push(Math.random() *500)
}
container_content = "";
function myFunction(element) {
container_content += '<div class="array-bar"></div>';
}
array.forEach(myFunction);
$(".container").html(container_content);
function another(element, index){
element.style.height = array[index]
}
$( "div" ).each( function( index, element ){
$( this ).css('height', array[index]);
});
$('button').click(function(){
var i, j, temp;
for(i = 0; i < array.length; i++)
{
for(j = 0; j < array.length-1; j++)
{
if( array[j] > array[j+1])
{
// swap the elements
temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
}
}
$( "div" ).each( function( index, element ){
$( this ).css('height', array[index]);
});
}
})
I took what you were trying to do and broke it down into 4 basic functions (1 main function and 3 helper functions).
runSort is the main function that uses all the other helper functions to do everything.
makeArrayAndUnsyncedBars generates your random array and basic divs that you'll use as green "bars", but it doesn't set the heights of these divs according to the values in the array.
syncArrayToBars sets the heights of these "bar" divs according to the values in the array
sortUntilNextSwap sorts the array until either a swap occurs or the sort completes. this function returns false if it made a swap (meaning that it's still working its way through the array) or true otherwise.
var FRAMES_PER_SECOND = 50;
var sortInterval = null;
function runSort() {
clearInterval(sortInterval);
makeArrayAndUnsyncedBars(50);
syncArrayToBars();
sortInterval = setInterval(function() {
var finishedSorting = sortUntilNextSwap();
syncArrayToBars();
if (finishedSorting) clearInterval(sortInterval);
}, Math.round(1000 / FRAMES_PER_SECOND));
}
var array = [];
function makeArrayAndUnsyncedBars(numberOfValues) {
var htmlToAdd = "";
array = [];
for (var i = 0; i < numberOfValues; i++) {
htmlToAdd += "<div class=\"bar\"></div>";
array.push(rando(150));
}
$("#bars").html(htmlToAdd);
}
function syncArrayToBars() {
for (var i = 0; i < array.length; i++) $(".bar").eq(i).css({
height: array[i] + "px"
});
}
var i, j, temp;
function sortUntilNextSwap() {
for (i = 0; i < array.length; i++) {
for (j = 0; j < array.length - 1; j++) {
if (array[j] > array[j + 1]) {
// swap the elements
temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
return false;
}
}
}
return true;
}
.bar {
width: 5px;
background: #5aedab;
border-radius: 3px;
display: inline-block;
margin: 0px 3px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://randojs.com/1.0.0.js"></script>
<div id="bars"></div>
<button onclick="runSort();">Run sort</button>
EDIT: I should mention that I used rando.js for the randomness out of habit, but it's not super necessary here given that you use Math.random() very little anyway. Take it out or leave it in according to your preference.
Related
I'm trying to build a visual representation of some famous sorting algorithms in javascript, but I can't understand why my code doesn't print each iteration even if the print function is in the for loop. I only get the final result.
This is the sorting function, in particular the selection sort algorithm:
function selectionSort(array) {
var i, j, min_idx;
let n = array.length;
for (i = 0; i < n-1; i++)
{
min_idx = i;
for (j = i + 1; j < n; j++)
{
if (array[j] < array[min_idx])
{
min_idx = j;
}
}
var temp = array[min_idx];
array[min_idx] = array[i];
array[i] = temp;
printArray(array);
}
}
And this is the printing function:
function printArray(array) {
document.getElementById('container').innerHTML ='';
for(let i = 0; i < array.length; i++)
{
document.getElementById('container').innerHTML += '<div class = "column '+i+'" id = "'+i+'" style = "height: '+array[i]+'px;"></div>';
}
}
Thank you a lot
It's what #Bravo states in the comments. The screen is updates at least 60 times per second, but it takes less time to do the sorting. So you need to add a timeout in a recursive loop so you can actually see the animation.
I replaced the first for loop with this recursive loop. I think the code it self-explanatory.
I did some optimization in your printArray(), where it takes time to constantly doing DOM changes. Instead, loop through to create a text string and then add it once to #container.innerHTML. There were also some faulty thinking in the value that you gave the visualized divs, where you only added the order (i), instead of adding the actual value (array[i]).
const iterationLegend = document.getElementById('iterations');
const containerDiv = document.getElementById('container');
const ANIMATION_SPEED = 1000;
const RESTART = 0;
var firstIteration;
function startSelectionSort(array) {
firstIteration = RESTART;
selectionSort(array);
}
function selectionSort(array) {
let min_idx = firstIteration,
n = array.length;
for (let j = firstIteration + 1; j < n; j++) {
if (array[j] < array[min_idx]) {
min_idx = j;
}
}
var temp = array[min_idx];
array[min_idx] = array[firstIteration];
array[firstIteration] = temp;
visualizeArray(array);
iterationLegend.textContent = 'iteration ' + firstIteration;
if (firstIteration < n - 1) {
firstIteration++;
setTimeout(selectionSort.bind(this, array), ANIMATION_SPEED);
} else {
iterationLegend.textContent = 'Done';
}
}
function visualizeArray(array) {
let elementStr = '';
let value = 0;
for (let i = 0; i < array.length; i++) {
value = array[i];
elementStr += `<div class="column${value}" data-value="${value}"></div>`;
}
containerDiv.innerHTML = elementStr;
}
startSelectionSort([2, 3, 5, 5, 1, 1, 1, 1, 4]);
fieldset {
display: inline;
}
#iterations {
font-size: 13px;
text-transform: uppercase;
}
#container {
display: inline-flex;
}
#container > div {
width: 10px;
height: 100px;
margin: 2px 1px 0px;
}
.column1 {
background-color: brown;
}
.column2 {
background-color: black;
}
.column3 {
background-color: teal;
}
.column4 {
background-color: red;
}
.column5 {
background-color: indigo;
}
<fieldset>
<legend id="iterations">Iterations</legend>
<div id="container"></div>
</fieldset>
I'm new here. I have spent the entire day trying to figure out what is wrong with my code. Yes, it might be a simple questions for you, since I just started JavaSript about a month ago. Anyways, your help in identifying the error in my code is greatly appreciated!
==========================================================================
The Question:
Code a function called extremeValue. It takes 2 parameters. The first is an array of integers (you do not need to validate this). The second parameter is either the String “Minimum” or “Maximum”. The function returns the minimum or maximum element value in the array depending on the second parameter’s value.
Do not use Math.min or Math.max. The standard algorithm for finding a minimum value in an array is to assume the first element (at index 0) is the current minimum. Then process the array repetitively from its second element to its last. On each iteration compare the current element being processed with the current minimum. If it’s less than the minimum set it as the current minimum. In this way at the end of processing the current minimum holds the minimum element value in the array. A similar process will work for finding the maximum. Test your code. (1 mark)
This function does two different jobs depending on its second parameter. What are the pros and cons of this approach to coding functions?
My Answer (which doesn't work):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Eng1003 Workshop Code Week 04</title>
<style>
#outputArea{
padding: .25em;
border: solid black 2px;
margin: 3em;
height: 20em;
width: 20em;
overflow-y: scroll;
font-family: arial "sans serif";
font-size: 1em;
color: rgb(50, 50, 250);
background-color: rgb(225,225,225) ;
}
</style>
</head>
<body>
<!-- -->
<div id="outputArea"></div>
<script>
function maximum(setOfValues){
var retVal = "" ;
var length = setOfValues.length ;
var max = 0 ;
for (var i = 0; i < length; i++){
if (setOfValues[i] > max){
max = setOfValues[i] ;
}
}
retVal = max ;
return retVal ;
}
function minimum(setOfValues){
var retVal = "";
var length = setOfValues.length ;
var min = 0 ;
for (var i = 0; i < length; i++){
if (setOfValues[i] < min){
min = setOfValues[i] ;
}
}
retVal = min;
return retVal ;
}
function extremeValue(setOfValues, command){
var outString = "" ;
var retVal = "" ;
if (command = "Maximum"){
outString = "The maximum value is " + maximum(setOfValues) + "." ;
} else if (command = "Minimum"){
outString = "The minimum value is " + minimum(setOfValues) + "." ;
} else {
outString = "Sorry, but your command is unclear. Please ensure that your input is either 'Maximum' or 'Minimum'." ;
}
retVal = outString ;
return retVal ;
}
var target = document.getElementById("outputArea") ;
var inputCommand = prompt("What is your command?") ;
var inputValues = [10,30,500, 1000] ;
var finalAnswer = "" ;
finalAnswer = extremeValue(inputValues, inputCommand) ;
target.innerHTML = finalAnswer ;
</script>
</body>
The problem is the way you're checking your prompted value:
if (command = "Minimum")
if (command = "Maximum")
Here you're assigning strings to command. The code should be (assuming we're using strict equality):
if (command === "Minimum")
if (command === "Maximum")
DEMO
In you HTML page, call the Result function as below:
<script>
Result.init();
</script>
var Result = function () {
var maximum = function (array) {
var val = 0;
for (var i = 0; i < array.length; i++) {
if (array[i] > val) {
val = array[i];
}
}
return val;
}
var minimum = function (array) {
var val = 0;
for (var i = 0; i < array.length; i++) {
val = array[i];
if (array[i] < val) {
val = array[i];
}
}
return val;
}
var start = function () {
var inputValues = [10, 30, 500, 1000];
var min = minimum(inputValues);
var max = maximum(inputValues);
}
return {
init: function () {
start();
}
};
}();
I'm new to frontend and I'm trying to practice doing this simple task: I have to create a grid on the fly that is n * n (n being inputed by the user).
I succesfully created a fix sized grid, but my problem is when trying to do this dynamically.
This is the code I wrote for a 3*3 grid: http://jsfiddle.net/y7c2h8yk/
For trying to create it dynamically I wrote the following function:
var setGridDimensions = function(n) {
// emptying current grid
$(".row").empty();
var $grid = $("#grid");
for (var i = 0; i < n; i++) {
// adding row
$grid.append('<div class="row">');
// adding each to element to row
**var $row = $(".row")[i];**
for (var j = 0; j < n; j++) {
$row.append('<div class="col"></div>');
}
}
};
Now, I understand there is a problem with line var $row = $(".row")[i]. What I need is inside the loop first create the row, then select the row created and then loop again and create each column. How can i do that ?
Any help would be really appreciated. Thanks.
You don't have to force jQuery to search for the .row element in the DOM tree n times. You have easy way to cache the element by setting it as variable.
Another thing, is that you should empty() the whole #grid element instead of .row. empty() method remove contents of the element, but not the element itself.
Alternatively, you could remove rows using $(".row").remove();
.empty() reference
.remove() reference
Code (I would however use the next one)
var setGridDimensions = function(n) {
var $grid = $("#grid").empty();
for (var i = 0; i < n; i++) {
// create .row and cache it setting as '$row' variable:
var $row = $('<div class="row"/>').appendTo($grid);
for (var j = 0; j < n; j++) {
$row.append('<div class="col"></div>');
}
}
};
DEMO
This would be faster than the one above, as it's single DOM modification:
var setGridDimensions = function(n) {
var html ='';
for (var i = 0; i < n; i++) {
html += '<div class="row">';
for (var j = 0; j < n; j++) {
html += '<div class="col"></div>';
}
html += '</div>';
}
// modify the DOM only once:
$("#grid").html(html);
};
DEMO
$(".row")[i] get the HTML element. So late, the $row.append('<div class="col"></div>'); will not work since .append() is a jQuery method.
If you want to select a specific index and keep it as a jQuery object, use .eq() :
var $row = $(".row").eq(i);
for (var j = 0; j < n; j++) {
$row.append('<div class="col"></div>');
}
Here's a way to do it without jQuery.
https://jsfiddle.net/lemoncurry/evxqybaL/1/
<div id="grid-holder"></div>
-
#grid-holder {
width: 100%;
}
.row {
clear: left;
background-color: red;
}
.cell {
width: 50px;
height: 50px;
border: 1px dashed blue;
float: left;
}
-
var gridly = function (n) {
var grid = document.getElementById("grid-holder");
for (var i = 0; i < n; i++) {
var row = document.createElement('div');
row.classList.add("row");
grid.appendChild(row);
for (var j = 0; j < n; j++) {
var cell = document.createElement('div');
cell.classList.add("cell");
row.appendChild(cell);
}
}
}
gridly(5);
use isotope http://isotope.metafizzy.co/ it uses the help of Javascript but it is very popular, so you will find plenty of docs
if you find it very complicated then there are many premium plugins that based their development on isotope already, for example the Media Boxes http://codecanyon.net/item/media-boxes-responsive-jquery-grid/5683020
I don't have any trouble grabbing a list of elements and sorting them alphabetically, but I'm having difficulty understanding how to do it with a modulus.
### UPDATE ###
Here's the code working 'my way', however, I like the re-usability of the answer provided below more, so have accepted that answer.
<script type="text/javascript">
$(document).ready( function() {
$('.sectionList2').each( function() {
var oldList = $('li a', this),
columns = 4,
newList = [];
for( var start = 0; start < columns; start++){
for( var i = start; i < oldList.length; i += columns){
newList.push('<li>' + $(oldList[i]).text() + '</li>');
}
}
$(this).html(newList.join(''));
});
});
</script>
For example. Say I have the following unordered list:
<ul>
<li>Boots</li>
<li>Eyewear</li>
<li>Gloves</li>
<li>Heated Gear</li>
<li>Helmet Accessories</li>
<li>Helmets</li>
<li>Jackets</li>
<li>Mechanic's Wear</li>
<li>Pants</li>
<li>Protection</li>
<li>Rainwear</li>
<li>Random Apparel</li>
<li>Riding Suits</li>
<li>Riding Underwear</li>
<li>Socks</li>
<li>Vests</li>
</ul>
I have this list set to display in 4 columns with each li floated right. Visually this makes finding items in larger lists difficult. The output I need is this:
<ul>
<li>Boots</li>
<li>Helmet Accessories</li>
<li>Pants</li>
<li>Riding Suits</li>
<li>Eyewear</li>
<li>Helmets</li>
<li>Protection</li>
<li>Riding Underwear</li>
<li>Gloves</li>
<li>Jackets</li>
<li>Rainwear</li>
<li>Socks</li>
<li>Heated Gear</li>
<li>Mechanic's Wear</li>
<li>Random Apparel</li>
<li>Vests</li>
</ul>
What I'm looking for is a function that I can pass my array of list items and get my array returned, sorted alphabetically, with a modulus of choice; in this case 4.
Any help would be appreciated as I can find no documentation on the subject.
Alphabetize your list. This is already done, in your case, but if not:
function alphabetizeElements(a, b)
{
var aText = $(a).text();
var bText = $(b).text();
return aText > bText ? 1 : aText < bText ? -1 : 0;
}
var alphabetizedList = $("#myList li").sort(alphabetizeElements);
Store the alphabetized index of each element:
$.each(alphabetizedList, function(i)
{
$(this).data("alphaIndex", i);
});
Sort the alphabetized list by modulus first, then index:
function listColumnSortFn(columns)
{
return function(a, b)
{
var aIndex = $(a).data("alphaIndex");
var bIndex = $(b).data("alphaIndex");
return ((aIndex % columns) - (bIndex % columns)) || (aIndex - bIndex);
}
}
var columnSortedList = alphabetizedList.sort(listColumnSortFn(4));
Replace the list elements with your sorted elements:
$("#myList li").remove();
$("#myList").append(columnSortedList);
Here is the whole thing, all together:
function sortList(columns)
{
var alphabetizedList = $("#myList li").sort(alphabetizeElements);
$.each(alphabetizedList, function(i)
{
$(this).data("alphaIndex", i);
});
var columnSortedList = alphabetizedList.sort(listColumnSortFn(columns));
$("#myList li").remove();
$("#myList").append(columnSortedList);
}
function alphabetizeElements(a, b)
{
var aText = $(a).text();
var bText = $(b).text();
return aText > bText ? 1 : aText < bText ? -1 : 0;
}
function listColumnSortFn(columns)
{
return function(a, b)
{
var aIndex = $(a).data("alphaIndex");
var bIndex = $(b).data("alphaIndex");
return ((aIndex % columns) - (bIndex % columns)) || (aIndex - bIndex);
}
}
$(function()
{
sortList(4);
});
var columnify = function (a,n) {
var result = [];
for (var i = 0, lastIndex = a.length - 1; i < lastIndex; i++)
result.push(a[i * n % (lastIndex)]);
result[lastIndex] = a[lastIndex];
return result;
}
var products = ["Boots",
"Eyewear",
"Gloves",
"Heated Gear",
"Helmet Accessories",
"Helmets",
"Jackets",
"Mechanic's Wear",
"Pants",
"Protection",
"Rainwear",
"Random Apparel",
"Riding Suits",
"Riding Underwear",
"Socks",
"Vests",]
columnify(products, 4)
["Boots", "Helmet Accessories", "Pants", "Riding Suits", "Eyewear", "Helmets", "Protection", "Riding Underwear", "Gloves", "Jackets", "Rainwear", "Socks", "Heated Gear", "Mechanic's Wear", "Random Apparel", "Vests"]
Apply that function to the already sorted list, and then it will return a list of strings in the order (almost) that you want. Then add the list that was returned in order to the unordered list in the DOM.
Also, I haven't tested it with anything besides that list. So I'd do that if I were you. From what I see, it only works if the length of the list is a multiple of n. Not that great of a solution but it's late for me and I can't be bothered to come up with anything better.
EDIT: fixed the issue with the last element
See if this will work: http://jsfiddle.net/6xm9m/2
var newList = new Array();
var listItem = $('#list > li');
var mod = 4;
var colCount = Math.ceil(listItem.length / mod);
listItem.each(function(index) {
var newIndex = ((index % colCount) * mod) + Math.floor(index / colCount);
// $(this).text(newIndex);
newList[newIndex] = this;
});
$('#list').empty();
for(var i = 0; i < newList.length; i++){
$('#list').append(newList[i]);
}
Needs improvements, probably, but I'm not really sure how well this works at all.
Here you go. The code is surprisingly simple once you figure it out. I realize you are using jQuery but I'm not familiar enough with it to use its features. This is simple enough that maybe it's not necessary.
function pivotArray(arr, columns) {
var l = arr.length, out = [], ind = 0, i = 0;
for (; i < l; i += 1) {
out[ind] = arr[i];
ind += columns;
if (ind >= l) {
ind = ind % columns + 1;
}
}
return out;
}
And here's the test to prove it works (tested in Firefox 3.6.9, IE 6, Chrome 1.0.154.36):
<html>
<head>
<style type="text/css">
a.panelnum {
display:block;
float:left;
width:40px;
height:40px;
border:1px solid black;
text-align:center;
vertical-align:middle;
text-decoration:none;
font-size:2em;
}
</style>
</head>
<body onload="doit(17, 4);">
<div id="output" style="border:1px solid blue;">
</div>
<script type="text/javascript">
function pivotArray(arr, columns) {
var l = arr.length, out = [], ind = 0, i = 0;
for (; i < l; i += 1) {
out[ind] = arr[i];
ind += columns;
if (ind >= l) {
ind = ind % columns + 1;
}
}
return out;
}
function doit(size, columns) {
document.getElementById('output').innerHTML = 'starting';
var l = size;
var inp = [];
for (var i = 0; i < l; i += 1) {
inp[i] = i;
}
var result = pivotArray(inp, columns);
var str = '';
for (i = 0; i < l; i += 1) {
str += '<a class="panelnum">' + result[i] + '</a>';
}
var d = document.getElementById('output')
d.innerHTML = '<p>Some pre text</p>' + str + '<p style="clear:both;">and some post text</p>';
d.style.width = (columns * d.childNodes[1].offsetWidth + 2) + 'px';
}
</script>
</body>
</html>
One more thing: it might be useful to just move the elements around in-place. I almost had script for it but my script was running backwards (as if floats went from top to bottom first). If I get time I'll work on it and post the code.
P.S. Anyone want to give me pointers on why I had to add 2 to the width calculation for IE6? Wait... it's the borders of the div isn't it?
What is the best way to give support nth-child in one shot to all IE version?
I want to give style like this. for some particular pages.
#products tr:nth-child(even) {
background-color: red;
}
#products tr:nth-child(odd) {
background-color: white;
}
You can do it in javascript.
var table = document.getElementById('products');
var rows = table.getElementsByTagName('tr');
for (var i = 0; i < rows.length; ++i)
{
if ( (i % 2) == 0 )
{
rows[i].className = 'even';
}
}
then do your CSS like this:
#products tr td
{
background-color: white;
}
#products tr.even td
{
background-color: red;
}
If you have used a javascript library, you could have done this :
$('#products tr:even').addClass('even');
That project gives you "native" support for these and many others CSS3 selectors for at least IE7/8.
But here you'll have a problem with IE7 which doesn't support background-color for tr.
I made something that should work in every browser:
https://gist.github.com/yckart/5652296
var nthChild = function (elem, num) {
var len = elem.length;
var ret = [];
// :nth-child(num)
if (!isNaN(Number(num))) {
for (var i = 0; i < len; i++) {
if (i === num - 1) return elem[i];
}
}
// :nth-child(numn+num)
if (num.indexOf("+") > 0) {
var parts = num.match(/\w/g);
for (var i = parts[2] - 1; i < len; i += parts[0] << 0) {
if (elem[i]) ret.push(elem[i]);
}
}
// :nth-child(odd)
if (num === "odd") {
for (var i = 0; i < len; i += 2) {
ret.push(elem[i]);
}
}
// :nth-child(even)
if (num === "even") {
for (var i = 1; i < len; i += 2) {
ret.push(elem[i]);
}
}
return ret;
};
var rows = document.getElementsByTagName('li');
var num = nthChild(rows, 2);
var formula = nthChild(rows, "3n+1");
var even = nthChild(rows, "even");
var odd = nthChild(rows, "odd");
// Note, forEach needs to be polyfilled for oldIE
even.forEach(function (li) {
li.className += " even";
});
odd.forEach(function (li) {
li.className += " odd";
});
formula.forEach(function (li) {
li.className += " formula";
});
num.style.backgroundColor = "black";
You can do this in jQuery too, and they'll likely have solved your cross browser issue.
$('#products').children('tr:even').css('background-color', 'red');
$('#products').children('tr:odd').css('background-color', 'white');