For a homework assignment, I have to make a web page that has a text area. The user inputs some text, hits the analyse button, and the output is supposed to be a list of the frequency of words of a given number of characters. For example, "one two three" would be two words of three characters and one word of five characters. The text area works fine, but I can"t seem to get the output to appear.
Here is the html:
<body>
<textarea id="text" rows="26" cols="80"></textarea>
<form>
<input type="button" id="analyse" value="Analyse Text">
</form>
<div id="output"></div>
</body>
The JavaScript file has 6 functions. The first function returns an array that stores the number of characters for each word in the input textarea:
function getWordInfo() {
var text = document.getElementById("text").value;
//the variable wordArray uses a regular expression to parse the input
var wordArray = text.split("/\w\w+/g");
var arrayLength = wordArray.length;
var charArray = [];
for (var i = 0; i < arrayLength; i++) {
var splitWord = wordArray[i].split("");
var wordLength = splitWord.length;
charArray.push(wordLength);
}
return charArray;
}
The second function is a simple object constructor that has a name property and a count property. The count property has a default value of 0.
function obCon(name,count) { //object constructor
this.name = name;
this.count = count;
count = typeof count !== "undefined" ? count : 0;
}
The third function returns an array that stores word objects. Each object has a name property and a count property. The name property is the number of characters in a word. The count property counts how many times an object with a given name property appears.
function arCon() { //array constructor
var charNum = getWordInfo();
var arrayLength = charNum.length;
var obArr = [];
for (var i = 0; i < arrayLength; i++) {
if (typeof obArr.indexOf( newOb.name === charNum[i] ) != "undefined" ) { // checks if the object needed exists
obArr.indexOf( newOb.name === charNum[i] ).count = obArr.indexOf( newOb.name === charNum[i] ).count + 1;
}else{
var newOb = new obCon(charNum[i]);
newOb.count = newOb.count + 1;
obArr.push(newOb);
}
}
return obArr;
}
The fourth function is a string formatter, meant format the objects from arCon into a single readable string, then store it in an array.
function formatter() {
var strAr = arCon();
var arrayLength = strAr.length;
var formatStr = [];
for (var i = 0; i < arrayLength; i++) {
var str = "Number of characters: " + strAr[i].name + ", Number of words with this length: " + strAr[i].count;
formatStr.push(str);
}
return formatStr;
}
The fifth function is called for an event handler, meant to handle the click of the analyse button. On click, it is meant to get the div tag, get the formatted array, then loop though each element in the array, where it creates a p element element, pulls the formatted string, sets the p element value to the formatted string, then appends the p element to the div tag.
function analyseButtonClick() {
var div = document.getElementById("output");
var str = formatter();
var arrayLength = str.length;
for (var i = 0; i < arrayLength; i++) {
var par = document.createElement("p");
var format = str[i];
par.value = format;
div.appendChild(par);
}
}
The sixth function is an init function that handles the button click.
function init() {
var button = document.getElementById("analyse");
button.onclick = analyseButtonClick;
}
window.onload = init;
I have run this though a validator, and it shows me that there are no syntax errors, so it must be a logic error. However, all the functions seem to do what they are supposed to do, so I am not sure where I went wrong.
edit1: ok, have replaced the third function with four new functions. a function that returns the highest number in the array returned by getWordInfo, a function that constructs objects with name properties from 2 up to that number, a function that update the count properties of the objects, and a function that removes unused objects. here they are:
function maxNum() {
var array = getWordInfo();
var num = Math.max.apply(Math, array);
return num;
}
function objArrCon() { //object array constructor
var num = maxNum();
var array1 = [];
for (var i = 2; i === num; i++) {
var myObj = new objCon(i,0);
array1.push(myObj);
}
return array1;
}
function objArrParse() { //updates the object with word info
var array1 = getWordInfo();
var array2 = objArrCon();
var loopLength1 = array1.length;
var loopLength2 = array2.length;
for (var i = 0; i < loopLength1; i++) {
for (var m = 0; m < loopLength2; m++) {
if (array2[m].name === array1[i]) {
array2[m].count++;
}
}
}
return array2;
}
function objArrTrun() { //removes unused objects
var array1 = objArrParse();
var loopLength = array1.length;
for (var i = 0;i < loopLength; i++) {
if (array1[i].count === 0) {
array.splice(i, array1);
}
}
return array1;
}
still does not work, but im getting there!
I have written this in jQuery because 1) it's not my homework assignment and 2) it would be good practice (for you) to translate it back into normal JavaScript. You'll need to rewrite the event handler, along with the two getters/setters ($("text area").val() and $("tbody").append()).
Your main problem is that you have made your solution too complicated! If all you want to do is display the number, Y, of words with length, X, then you should do the following:
Grab the value of the text area and store in text
Remove all non-alphanumeric characters.
Split the text string wherever one or more spaces is present and store it in text
Loop through the array and map each array element, based on its length, to wordMap
Loop through wordMap and append it to wherever in the DOM you want the results to be
fiddle
JavaScript
$("#analyze").click(function () {
var text = $("textarea").val().replace(/[^\d\w ]+/g," ").split(/[ ]+/);
// this grabs the value of the text area, replaces all non-numerical
// and non-alphabetical characters with "", and then splits it into an array
// wherever one or more spaces is present.
var wordMap = {};
// makes an object that will be used as an associative array
text.forEach(function (d) {
// loops through the array
// if there is no key called d.length in wordMap
if (!wordMap[d.length])
wordMap[d.length] = 1; // set its count to one
else
wordMap[d.length]++; //otherwise, increment it
})
// loop through all the properties of wordMap
for (var x in wordMap) {
$("tbody").append("<tr><td>" + x + "</td><td>"
+ wordMap[x] + "</td></tr");
}
console.log(wordMap);
})
HTML
<textarea placeholder="type something..."></textarea>
<button id="analyze">Click to analyze</button>
<div id="results">
<table>
<thead>
<th>Length of Word</th>
<th>Number of Occurrences</th>
</thead>
<tbody>
</tbody>
</table>
</div>
Related
Forgive me, I am not quite skilled and fairly new to the language and programming practices. I am creating a button which prompts the user to enter a number once the number is entered, I create a for loop that iterates through the same amount as the number. For example, the user enters 4 and the screen will display 0 1 2 3 and then I have a button that asks the user to enter a number to see if that number exists in the previous array. So if the user entered 3 it would dispay "it exists" if they entered 5 it would display "number not found". Should I create an array to store the iterations and then run that array through a function that searches for the number. Looking for guidance, thank you for the help guys.
function getNumber() {
var el = document.getElementById("demo");
// Get the user's input and convert it to a number
var n = parseInt(prompt("Please enter a number"),10);
// Set up a string that will become the output.
var output = " ";
// loop through given number
for(var i = 0; i < n; i++){
// variable containing iterations
output += i;
//var numArray[i] = output;
}
//display iterations
el.textContent = output;
}
function findNumber(){
var sn = parseInt(prompt("Search for number"),10);
for(var j = 0; j < sn; j++){
}
}
<button onclick="getNumber()">Click!</button>
<p id="demo"></p>
<button onclick ="findNumber()">Click!</button>
Make your n variable global
Than compare if sn is higher than n
var n; // Make it global
function getNumber() {
var el = document.getElementById("demo");
// Get the user's input and convert it to a number
n = parseInt(prompt("Please enter a number"), 10);
// Set up a string that will become the output.
var output = " ";
// loop through given number
for (var i = 0; i < n; i++) {
// variable containing iterations
output += i;
//var numArray[i] = output;
}
//display iterations
el.textContent = output;
}
function findNumber() {
var sn = parseInt(prompt("Search for number"), 10);
var isHigher = sn > n; // n is now accessible in this function
var message = isHigher ? "Not found" : "Number found!";
alert( message );
}
<button onclick="getNumber()">Click!</button>
<p id="demo"></p>
<button onclick="findNumber()">Click!</button>
To search an array, use the indexOf() method of a JavaScript array. The original post gives an example populating the array with myArray[x]=x, creating options pointed out by other solutions. Presuming you want to search a more general case of an array, you could use indexOf directly or define a function that returns true or false:
function inArray(myArray, queryValue) {
return myArray.indexOf(queryValue) > -1;
};
Arrays in JavaScript are objects with some additional methods like pop(), indexOf(), etc. JavaScript objects are associative arrays; this solution only applies to Array objects. Array objects are constructed with the [] literal or the Array() constructor. Arrays can only have properties named by ints, unlike other JavaScript associative arrays. See Eloquent JavaScript.
In this theoretic example, it's true that you only need to check if the second number entered is smaller than the first number. If you want to search for a number in an array of any numbers, you can use the javascript indexOf function. See example below:
var arr = [1,6,77,888];
if (arr.indexOf(66) > -1) {
alert('number is in array');
} else {
alert('number is not in array');
}
There are a couple of ways to do this. For the sake of simplicity, I'll use a global variable here.
// A global variable to store user input
var userInput;
function getNumber() {
var el = document.getElementById("demo");
// Get the user's input and convert it to a number
var n = parseInt(prompt("Please enter a number"),10);
// Store the user's input to our global variable
userInput = n;
// Set up a string that will become the output.
var output = " ";
// loop through given number
for(var i = 0; i < n; i++){
// variable containing iterations
output += i;
//var numArray[i] = output;
}
//display iterations
el.textContent = output;
}
function findNumber(){
var el = document.getElementById("result");
var sn = parseInt(prompt("Search for number"),10);
// If number is within the range
if (sn < userInput) {
el.textContent = "it exists";
}
// If number is not within the range
else {
el.textContent = "number not found";
}
}
<button onclick="getNumber()">Click!</button>
<p id="demo"></p>
<button onclick ="findNumber()">Click!</button>
<p id="result"></p>
I'm trying to find an index of a number in a 2d array, but console gives out
Uncaught TypeError: block[((a * 10) + c)].indexOf is not a function
I think it has something to do with the way of accessing the array element, but can't seem to find the problem.
Here's the code.
var block = [];
var temp;
var del;
for(var a = 0;a < 9;a++){
for(var b = 0;b < 9;b++){
temp = parseInt(prompt("enter element number " + b + " of row number " + a));
console.log(temp);
if(temp>0){
block[a*10+b] = temp;
}else{
block[a*10+b] = [1,2,3,4,5,6,7,8,9];
}
// console.log(block[a*10+b]);
}
}
for(var a = 0;a < 9;a++){
for(var b = 0;b < 9;b++){
if(typeof(block[a][b]) == "number"){
for(var c = 0;c < 9;c++){
if(c != b){
del = block[a*10+c].indexOf(b);
block[a*10+c].splice(del,1);
}
}
}
}
}
You have a mix of data types assigned to the block array. When the user enters a value that is not numeric, you assign indeed a nested array to one of the block elements, but not so when the user enters a valid number.
From what I think you are doing (a Sudoko game?) this might be intended: the numbers are known values in the grid, the nested arrays represent a list of values that are still possible at that particular cell.
But then in the second part of your code, you should check in which of the two cases you are, as you only want to remove array elements if the value you are looking at is indeed an array. This test you can do with Array.isArray().
There are also some other issues in the second part of your script:
The expression block[a][b] is not consistent with how you have filled that array: it should be block[a*10+b] to be consistent.
the b in .indexOf(b) is wrong: you are not looking for that value, but for block[a*10+b].
the splice() is always executed, even if the indexOf returned -1. This leads to an undesired effect, because if the first argument to splice() is negative, the index really is counted from the end of the array, and still an element is removed from the array. This should not happen: you should only execute the splice if the indexOf result is non-negative.
Below I have put a working version, but in order to avoid the almost endless prompts, I have provided this snippet with a textarea where you can input the complete 9x9 grid in one go, and then press a button to start the execution of your code:
document.querySelector('button').onclick = function () {
var block = [];
var temp;
var del;
var text = document.querySelector('textarea').value.replace(/\s+/g, '');
for(var a = 0;a < 9;a++){
for(var b = 0;b < 9;b++){
temp = parseInt(text[a*9+b]); // <-- get char from text area
if(temp>0){
block[a*10+b] = temp;
}else{
block[a*10+b] = [1,2,3,4,5,6,7,8,9];
}
}
}
for(var a = 0;a < 9;a++){
for(var b = 0;b < 9;b++){
var num = block[a*10+b]; // <-- get content, fix the index issue
if(typeof num == "number"){
for(var c = 0;c < 9;c++){
if(c != b && Array.isArray(block[a*10+c])){ //<-- add array-test
del = block[a*10+c].indexOf(num); // <-- not b, but num
if (del > -1) // <-- only splice when found
block[a*10+c].splice(del,1);
}
}
}
}
}
document.querySelector('pre').textContent = 'block='+ JSON.stringify(block);
};
<textarea rows=9>
53..7....
6..195...
.98....6.
8...6...3
4..8.3..1
7...2...6
.6....28.
...419..5
....8..79
</textarea>
<button>Process</button>
<pre></pre>
Note that there are elements in block which remain null. I suppose you intended this: as you multiply a with 10, and only store 9 values per "row", there is always one index that remains untouched.
I haven't looked over your second for loop, but you can try applying similar logic there as in the snippet I've provided. The issue is that you need to create a temporary array inside the outer for loop over values of a (but NOT inside the inner, nested for loop over values of b). Inside the for loop for values of b, then, you need to push something into that temporary array (which I called temp). Then, outside of the b for loop, but before the next iteration of a, push that temporary array temp to the block array. In this way, you will generate a 2D array.
var block = [];
var del;
for(var a = 0; a < 9; a++) {
let temp = [];
for(var b = 0; b < 9; b++) {
let num = parseInt(prompt(`Enter element ${b} of row ${a}:`));
if (num > 0) {
temp.push(num);
} else {
// block[a*10+b] = [1,2,3,4,5,6,7,8,9];
temp.push(b);
}
}
block.push(temp);
}
how do I count the frequency of the elements in the array, I'm new to Javascript and completely lost, I have looked at other answers here but can't get them to work for me. Any help is much appreciated.
function getText() {
var userText;
userText = document.InputForm.MyTextBox.value; //get text as string
alphaOnly(userText);
}
function alphaOnly(userText) {
var nuText = userText;
//result = nuText.split("");
var alphaCheck = /[a-zA-Z]/g; //using RegExp create variable to have only alphabetic characters
var alphaResult = nuText.match(alphaCheck); //get object with only alphabetic matches from original string
alphaResult.sort();
var result = freqLet(alphaResult);
document.write(countlist);
}
function freqLet(alphaResult) {
count = 0;
countlist = {
alphaResult: count
};
for (i = 0; i < alphaResult.length; i++) {
if (alphaResult[i] in alphaResult)
count[i] ++;
}
return countlist;
}
To count frequencies you should use an object which properties correspond to the letters occurring in your input string.
Also before incrementing the value of the property you should previously check whether this property exists or not.
function freqLet (alphaResult) {
var count = {};
countlist = {alphaResult:count};
for (i = 0; i < alphaResult.length; i++) {
var character = alphaResult.charAt(i);
if (count[character]) {
count[character]++;
} else {
count[character] = 1;
}
}
return countlist;
}
If you can use a third party library, underscore.js provides a function "countBy" that does pretty much exactly what you want.
_.countBy(userText, function(character) {
return character;
});
This should return an associative array of characters in the collection mapped to a count.
Then you could filter the keys of that object to the limited character set you need, again, using underscore or whatever method you like.
Do as below:
var __arr = [6,7,1,2,3,3,4,5,5,5]
function __freq(__arr){
var a = [], b = [], prev
__arr.sort((a,b)=>{return a- b} )
for(let i = 0; i<__arr.length; i++){
if(__arr[i] !== prev){
a.push(__arr[i])
b.push(1)
}else{
b[b.length - 1]++
}
prev = __arr[i]
}
return [a , b]
}
I am trying to write a code in javascript/jquery and html which I thought would be fairly simple, but turns out to be quite challenging (to me). I have a program which computes the first x numbers of the fibonacci sequence, and stores it in an array. What I am trying to do is make two buttons that will display the next or previous number in the sequence. This is what I have so far.
Javascript:
var all = new Array();
fib = function (numMax) {
for (i = 0, j = 1, k = 0; k < numMax; i = j, j = x, k++) {
x = i + j;
//window.document.write(x + " ");
all[k] = x;
}
};
fib(1000);
fibon = function () {
getElementById("mynum").innerHTML = "all[+1]";
};
HTML:
<input type="text" id="mynum">
<button onclick="fibon();">Next</button>
You need a variable that contains the current index, and then increment it each time you click.
fibindex = 0;
function fibon() {
if (fibindex >= all.count) {
document.getElementById("mynum").value = "We've run out of Fibonacci numbers";
} else {
document.getElementById("mynum").value = all[fibindex];
fibindex++;
}
}
Also, notice that you should not put quotes around a use of a variable. Add you use .value to fill in an input, not .innerHTML.
DEMO
I wrote a simple program to analyze a string to find the word with the greatest amount of duplicate letters within it. It essentially takes a given string, breaks it up into an array of separated words, and then breaks up each separate word into alphabetically sorted groups of individual letters (which are then compared as prev and next, 2 at a time, as the containing array is iterated through). Any two adjacent and matching values found adds one tally to the hash-file next to the word in question, and the word with the most tallied pairs of duplicate letters is returned at the end as greatest. No matching pairs found in any word returns -1. This is what it's supposed to do.
Below, I've run into a problem: If I don't use a REGEXP to replace one of my matched characters, then my code gives false positives as it will count triplicates (eg, "EEE"), as two separate pairs, (eg, "EEE" = "EE & EE", instead of being viewed as "EE, E"). However, if I DO use the REGEXP below to prevent triplicate counts, then doing so breaks my loop mid-stride, and skips to the next word. Is there no way to make this way work? If not, would it be better to employ a REGEXP which deletes all chars EXCEPT the duplicate characters in question, and then perhaps I could divide the .length of each word by 2 to get the number of pairs remaining? Any ideas as to how to solve this would greatly help.
var str = "Helloo aplpplpp pie";
//var str = "no repting letrs";
//var str = "ceoderbyte";
function LetterCountI(str) {
var input = str.split(" ");
console.log(input);
console.log("\n")
var hashObject = {};
var word = "";
var count = 0;
for(var i = 0; i<input.length; i++) {
var currentItem = input[i];
var currentWordIntoChars = currentItem.split("").sort();
console.log(currentWordIntoChars);
var counter = 0;
for(var j=1; j<currentWordIntoChars.length; j++) {
console.log(currentWordIntoChars[j-1] + "=currentChar j-1");
console.log(currentWordIntoChars[j] + "=prev j");
console.log("-");
var final = currentItem;
if(currentWordIntoChars[j-1] == currentWordIntoChars[j]) {
counter++;
hashObject[final] = counter;
//currentWordIntoChars = currentWordIntoChars[j-1].replace(/[a-z]/gi, String.fromCharCode(currentItem.charCodeAt(0)+1));
//HERE REPLACE j-1 with random# or something
//to avoid 3 in a row being counted as 2 pair
//OR use regexp to remove all but pairs, and
//then divide .length/2 to get pairs.
console.log(counter + " === # total char pairs");
}
if(count<hashObject[currentItem]) {
word = final;
count = hashObject[currentItem];
}
}
}
console.log(hashObject);
console.log("\n");
for (var o in hashObject) if (o) return word;
return -1;
}
console.log(LetterCountI(str));
An other way to do it, consists to replace duplicate characters in a sorted word:
var str = "Helloo aplpplpp pie";
function LetterCountI(str) {
var input = str.split(" ");
var count = 0;
var result = -1;
for(var i = 0; i<input.length; i++) {
var nb = 0;
var sortedItem = input[i].split("").sort().join("");
sortedItem.replace(/(.)\1/g, function (_) { nb++ });
if (nb > count) {
count = nb;
result = input[i];
}
}
return result;
}
console.log(LetterCountI(str));
Notes: The replace method is only a way to increment nb using a callback function. You can do the same using the match method and counting results.
if two words have the same number of duplicates, the first word will be returned by default. You can easily change this behaviour with the condition of the if statement.
Whenever you find a match within a word, increment j by 1 to skip comparing the next letter.
var str = "Helloo aplpplpp pie";
//var str = "no repting letrs";
//var str = "ceoderbyte";
function LetterCountI(str)
{
var input = str.split(" ");
console.log(input);
console.log("\n")
var hashObject = {};
var word = "";
var count = 0;
for(var i = 0; i<input.length; i++)
{
var currentItem = input[i];
var currentWordIntoChars = currentItem.split("").sort();
console.log(currentWordIntoChars);
var counter = 0;
for(var j=1; j<currentWordIntoChars.length; j++)
{
console.log(currentWordIntoChars[j-1] + "=currentChar j-1");
console.log(currentWordIntoChars[j] + "=prev j");
console.log("-");
var final = currentItem;
if(currentWordIntoChars[j-1] == currentWordIntoChars[j])
{
counter++;
hashObject[final] = counter;
j++; // ADD HERE
console.log(counter + " === # total char pairs");
}
if(count<hashObject[currentItem])
{
word = final;
count = hashObject[currentItem];
}
}
}
console.log(hashObject);
console.log("\n");
for (var o in hashObject) if (o) return word;
return -1;
}
console.log(LetterCountI(str));