JavaScript Undefined when alert(array.length) is called [duplicate] - javascript

This question already has answers here:
Length of a JavaScript object
(43 answers)
Closed 2 years ago.
Lately I have been trying to create a webpage with a search feature. My way of implementing this, while not the fastest or most elegant, should work in theory. All it does is split the search term into a list, the delimiter being a space, and then splits the keywords (in dictionary format, with the value being a download link, and with the key being the "keywords" I was referring to) and finally, it has an outer loop looping through the keys (being split each iteration into a list), and an inner loop looping through the words input through the input field. If a word in the search field matches one keyword of the key words list, then that key from the dictionary gets a score of +1.
This should sort the keys into order of best result to worst, and then the code can continue on to process all this information and display links to the downloadable files (the point of the webpage is to supply downloads to old software [of which I have collected over the years] etc.). However, when I run the program, whenever the alert(ranking.length) function is called, all I get is undefined in the output window.
Here is the code. (The search() function is called whenever the search button is pressed):
var kw_href = {
"windows":["windows3.1.7z"],
"ms dos 6.22":["ms-dos 6.22.7z"]
}
function search(){
var element = document.getElementById("search_area");
var search_term = element.value.toLowerCase();
var s_tags = search_term.split(" ");
var keys = Object.keys(kw_href);
ranking = {
"windows":0,
"ms dos 6.22":0
};
for (i = 0; i < keys.length; i++){
keywords_arr = keys[i].split(" ");
for (x = 0; x < s_tags.length; x++){
if (keywords_arr.includes(s_tags[x])){
ranking[keys[i]] = ranking[keys[i]] + 1;
}
}
}
// now we have a results list with the best results. Lets sort them into order.
alert(ranking.length);
}
Edit
alert(ranking.length) line is for debugging purposes only, and I was not specifically trying to find the length.

ranking is a generic object, not an array, so it won't have a computed length property.
If you want to count the number of properties in it, convert it to an array with Object.keys(ranking).

ranking should be array of object like ranking =[{"windows":0,"ms dos 6.22":0},{"windows":1,"ms dos 6.22":10}]
Then length ranking.length will work

Related

keeping localStorage objects that iterate whilst using clear() within the same function

I'm trying to clear all local storage when the user either completes the game loop or starts a new game, but also keep some values.
I can do this already with my sound values for volume:
// inside a conditional statement that fires when the user chooses to start a new game.
if (newGameBool === '1') {
var tst = myAu;
//myAu is the stored value that the user sets as sound using a range type input
localStorage.clear();
localStorage.setItem("Au", tst);//A newly cleared localStorage just got a new value, and it's the same as it was before.
UI.myLoad();//reload the function that uses LS to do things.
}
How do I do this for key's that have an iterating number attached to them?
Here is how I save them:
var i = +v + +1;
localStorage.setItem("v", i);
var vv = localStorage.getItem("v");
localStorage.setItem("LdrBrd_" + vv, JSON.stringify(LdrBrd));//saves all data with the iterating key name.
Calling them the way i did the sound function:
var gv = v + 1;//v calls the value from LS and adjusted for off-by-one error. gv is a local variable.
if (newGameBool === '1') {
var ldd, vg;
for (var ii = 0; ii < gv; ii++) {
var ld = localStorage.getItem("LdrBrd_" + ii);
if (ld != null) {
//these are the values that i want to pass beyond the clear point
ldd = JSON.parse(ld);//JSON string of data saved
vg = ii;//how many of them.
}
}
localStorage.clear();
for (var xx = 0; xx < vg; xx++) {
var nld = localStorage.getItem("LdrBrd_" + xx);
if (nld != null) {
localStorage.setItem("LdrBrd_" + ii, JSON.stringify(ldd));
}
}
localStorage.setItem("v", vg);
UI.myLoad();
}
I have been using console.log() in various spots to see what is going on. I comment-out the clear function just to see if the values were wrong and they don't save all all. I tried to make a fiddle, but the local storage wasn't working at all there. In visual studio, it works fine but the script to this file is almost 2000 lines long, so i tried to dress it up the best i knew how.
Thanks in advance for any help or guidance.
I was stuck on this for a few days, but i think i found something that will work, so i'll answer my own question in case there is value in posterity.
locatStorage.clear();
/* ^LS clear() function is above all new setItem codes, some variables are declared globally and some are declared at the top of the functional scope or as param^ */
var itemClass = document.querySelectorAll(".itemClass");//the strings are here
if (itemClass) {//make sure some exist
for (var p = 0; p < itemClass.length; p++) {//count them
mdd = JSON.parse(itemClass[p].innerText);//parse the data for saving
localStorage.setItem("v", v);//this is the LS item that saves the amount of items i have, it's declared at the top of the functions timeline.
localStorage.setItem("LdrBrd_" + p, JSON.stringify(mdd));//this setItem function will repeat and increment with 'p' and assign the right string back to the key name it had before.
}
}
The key is to keep the strings physically attached to an element, then call the class name. The i ran a loop counting them. 'mdd' will spit back each item i want. So then all that is left to do is re-set the item back to it's original status.
This has allowed me to create a way for my users to collect trophies and keep them even after clearing the localStorage when the he/she decides to start a new game.
I use CSS to hide the text from the string.
color:transparent;
In my gameLoop, i have a function that will read the saved strings and show them as cards just below the hidden strings.
Since you want to keep some values I recommend one of two things:
Don't call localStorage.clear() and instead only wipe out the values that you want using localStorage.removeItem('itemName'). Since you said the item names have a numeric component, maybe you can do this in a loop to reduce code.
Pull item(s) that you want saved first and restore them after calling clear(). This option is best if there are way more items that you want removed rather than saved (see below)
function mostlyClear() {
var saveMe = {};
saveMe['value1'] = localStorage.getItem('value1');
saveMe['anotherValue'] = localStorage.getItem('anotherValue');
localStorage.clear();
for(var prop in saveMe) {
if(!saveMe.hasOwnProperty(prop)) continue;
localStorage.setItem(prop, saveMe[prop]);
}
}

Using # to mark end of array

I am currently studying another user’s code for a coding question from LeetCode. My question is about certain aspects of his code. Here’s a link to the question.
Question:
Why does this user use a # to mark the end of the array?
Under the second if case, the user writes:
ans.push(nums[t] + '->' + (nums[i-1]))
Now, I understand what this statement does. My question is: Why does this produce an output of ["0->2",...] instead of [0"->"2,...]?
var summaryRanges = function(nums) {
var t = 0
var ans = []
nums.push('#')
for(var i=1;i<nums.length;i++)
if(nums[i]-nums[t] !== i-t){
if(i-t>1)
ans.push(nums[t]+'->'+(nums[i-1]))
else
ans.push(nums[t].toString())
t = i
}
return ans
}
The algorithm depends on that the difference between nums[i] and nums[t] is not the same as the difference between i and t. When that happens, the algorithm adds more to the output. This creates a special case when the last range is just a single number, since this cannot trigger the condition.
Hence the hash character is padding to extend the array in order to make the algorithm work, so that the condition nums[i]-nums[t] !== i-t will trigger even for a finishing range of a single number. It could be any string really as long as it is not an integer number.

Matching Array to JavaScript Matrix

I’m wondering how to solve a matching/lookup problem and I “think” a multi-dimensional array is the solution. In short, I want to match a list of comma separated SKUs stored as a cookie value against a finite list of SKUs with matching product names and print out the matched product names onto the page. I’m not sure if this is the best way to do this, but with what I have so far I’m not clear how to properly breakup the comma separated strings from the cookie (right now it’s trying to match the entire cookie value), match them to the matrix (17 total rows) and then print out the Product Name.
<script>
var staticList = [
[“1234”, “Chocolate Ice Cream”],
[“1235”, “Peanut Butter Cookie”],
[“6G2Y”, “Raspberry Jell-O”],
[“YY23”, “Vanilla Wafers”]
];
var cookieSkus = [‘1235,YY23’]; // comma separated value from cookie
jQuery(function () {
for (var i = 0; i < staticList.length; i++) {
if (cookieSkus.indexOf(staticList [i][0]) > -1) {
jQuery('#pdisplay).append(staticList [i] [1] + '<br />');
}
}
});
</script>
<p id=”pdisplay”></p>
In this example, the paragraph "pdisplay" would contain:
Peanut Butter Cookie
Vanilla Wafers
Is there a way to correct what I have above or is there a better method of accomplishing what I’m trying to do?
First, you might want to focus on the Cookie SKUs rather than the staticList. The reason for this is that the cookie may have a variable number, and may be as small as 0 elements. (After all, we don't need to list the items if there are no items).
This may be accomplished simply by converting the string to an array and then checking if the SKU is in the staticList. Unfortunately, since you are using a multidimensional array, this would require going through the staticList for each cookie sku. Using just this suggestion, here is a basic example and fiddle:
Rewrite: Accounting for the fact that staticList is an Array of Arrays
jQuery(function() {
var skus = cookieSkus[0].split(',');
for (var i = 0; i < skus.length; i++) {
for (var j = 0; j < staticList.length; j++) {
if (staticList[j][0] == skus[i]) {
jQuery('#pdisplay').append(staticList[j][2] + '<br/>');
break; // Will end inner if the item is found... Saves a lot of extra time.
}
}
}
});
Edit 2: Using an Object (A possibly better approach)
According to the comments, you must support IE8. In this case, you might consider an Object instead of a multi-dimensional array. The reasons for this are as follows:
An object is actually an associative array (with a few perks).
You can directly check for property existence without having any nested arrays.
Object property access is typically faster than looping through an array
You can access object properties nearly exactly like accessing an array's elements.
When using an Object, the original version of my code may be used without modification. This is because the object's structure is simpler. Here is a fiddle for you: option 2
var staticList = {
"1234": "Chocolate Ice Cream",
"1235": "Peanut Butter Cookie",
"6G2Y": "Raspberry Jell-O",
"YY23": "Vanilla Wafers"
};
jQuery(function() {
var skus = cookieSkus[0].split(',');
for (var i = 0; i < skus.length; i++) {
if (staticList[skus[i]])
jQuery('#pdisplay').append(staticList[skus[i]] + '<br/>');
}
});
Responding to your comment:
The reason that the output matches what is desired is because unlike an array which has numerical indices, the object's indices are the actual skus. So, there is no staticList[0] if staticList is an object. Instead (in the context of the staticList object), 1234 = "Chocolate Ice Cream". So, an object definition basically goes as follows:
var objectName = {
index1: value1,
index2: value2,
...,
...
}
The index may be any primitive value (integer or string). The value may be any valid javascript value including a function or an inner object. Now, to get the value at a specific index, you may do either:
objectName.index1 (no quotes)
OR:
objectName["index1"] (quotes needed if the index is a string)
The result of either of those will be:
value1
It's as simple as that.
I would try something like this:
var cookieSkus = cookieSkus[0].split(',');
staticList.filter(function(cell){
return cookieSkus.some(function(val){return cell[0] === val; });
}).map(function(cell){
jQuery('#pdisplay).append(cell[1] + '<br />');
});
Disclaimer: provided based on the sample code provided above along with recent comments

Shuffle a Questions array and an Answers multidimensional array the same

Alright, so the thing is;
Now I made this website for myself to study japanese more easily, by making a list of japanese words and its definition. I put the japanese words in lesson1.txt and the answers in lesson1answers.txt in the same order. I read them out in php and put them in arrays in javascript.
Now the problem is is that these arrays have the same order every time I start the site, which is quite annoying for me as I keep resembling the spot to the answer, and I dont even read the japanese word (Damn our human brains!). So I figured the best way to solve this is by randomizing the elements of both the arrays, but they both have to be randomized in the same pattern because else the question doesnt correspond correctly to the answer anymore.
One of the arrays I have is multidimensional. The array first holds elements that represent the lines I read out of a .txt file. The line has multiple characters in it, consequently the second element of the array represents each character (Dont ask me why I did this, it had to do with me not being able to read out japanese characters in php and put them in js directly). The other array is just the array I keep the answers in.
So I have AnswersArray(); and JapaneseCharactersArray();
I've been stuck on this problem for a whole day now, someone please help or give a hint..
You should merge them into one array of objects:
// assuming AnswersArray.length == JapaneseCharactersArray.length
var array = [];
for (var i=0; i<AnswersArray.length; i++)
array[i] = {
answer: AnswersArray[i],
japanese: JapaneseCharactersArray[i]
};
Now you have only one array, each item containing all information on a single word. You can easily shuffle it now.
Instead of AnswersArray[x] you then would use array[x].answer further down in your code.
Of course, you could just adapt your fisherYates function to shuffle two or more arrays in the same way:
function fisherYates(first/*, ... arrays */) {
var i = first.length,
argl = arguments.length;
if (i == 0) return false;
while (--i) {
var r = Math.floor(Math.random() * (i + 1));
for (var j=0; j<argl; j++) {
var tempi = arguments[j][i];
var tempj = arguments[j][r];
arguments[j][i] = tempj;
arguments[j][r] = tempi;
}
}
}
// usage:
> fisherYates(AnswersArray, JapaneseCharactersArray);
Although the best way is probably to just create them as a single array, another way is to create a new array, of the same length as the others, where each item in the array is a number: 1, 2, 3 ... n. But instead of having the numbers in order, use Math.random() to randomize their order.
Then loop through that new array as such:
for(i=0; i<AnswersArray.length; i++) {
AnswersArray[RandomArray[i]]; // whatever
}
Another way is to just swap the entire row when you randomize the array.

How do I choose a random value from an array with JavaScript? [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
JavaScript: Getting random value from an array
I have an external js with the following line:
var postmessage = "hi my favorite site is http://google.com";
but is there a way to pick a site a random from an array so like this
var postmessage = "hi my favorite site is +'random'";
random= http://google.com, http://yahoo.com, http://msn.com, http://apple.com
how do i make it work?
var favorites = ["http://google.com", "http://yahoo.com", "http://msn.com", "http://apple.com"];
var favorite = favorites[Math.floor(Math.random() * favorites.length)];
var postmessage = "hi my favorite site is " + favorite;
Create an array of your sites, then pick one element from the array. You do this by choosing a random number using Math.random(), which gives you a result greater than or equal to 0 and less than 1. Multiply by the length of your array, and take the floor (that is, take just the integer part, dropping off any decimal points), so you will have a number from 0 to one less than the length of your array (which will thus be a valid index into your array). Use that result to pick an element from your array.
var sites = new Array('http://www.google.com', "http://www.stackoverflow.com")
var postmessage = "hi my favorite site is" + sites[Math.round(Math.random()*(sites.length-1))];
First stick all of your sites in an array. Then get a random number from the array length (the -1 is because an array is zero indexed and the length that is returned starts at 1)
Do something like this:
function getRandomSite(){
var sites = ["google.com","bing.com","xyz.com","abc.com","example.com"];
var i = parseInt(Math.random()*(sites.length-1));
return sites[i];
};

Categories