For my Javascript course I got this code:
for (var i in window.navigator)
{
document.getElementById('divResult').innerHTML +=
i + ': ' + window.navigator[i] + '<br />';
}
</script>
The teacher (online) wants me to limit the results into maximal 10.
For me this is a big puzzle. From other questions about the for..in I think to know it is a discussable statement. But how to approach this for..in? As an array with i.length?
Just set a counter. In each iteration you increase the counter and when the counter reaches 10 you simply break out of your loop
Code:
// Since we're going to access this div multiple times it's best to
// store it outside of the for loop.
var output = document.getElementById('divResult');
var counter = 0;
for (var elem in window.navigator) {
var value = window.navigator[elem];
output.innerHTML += counter + ': ' + elem + '=' + value + '<br />';
++counter;
if (counter == 10) {
break;
}
}
Since you're new to JavaScript I would like to explain a little bit about for-in
If you want to get a specific value from an array you access it's element by index. So for example:
var myArray = [7, 5, 6, 6];
for (var i = 0; i < myArray.length; ++i) {
var value = myArray[i];
}
But now you want to loop through window.navigator and this element is not an array but a object. And since a object is key-value it does not have a index. So how do you loop through it?
Let's imagine window.navigator looks like this:
var navigator = {
myBrowser: 'Google Chrome',
myOtherProperty: 'otherValue',
AnotherProperty: 'anotherValue'
};
If we want to get the first element from our object we use
navigator.myBrowser
or
navigator['myBrowser'];
Now we want to loop through all the elements in our object. Since the normal for loop uses a index and objects don't have indexes we use the for in loop. This loop iterates through all the properties of our object and gives us the key.
for (var key in navigator) {
// Here we access a property in our object by the key given by our for loop.
var value = navigator[key];
}
So the first iteration our key is myBrowser and the value is Google Chrome
The next iteration the key is myOtherProperty and the value otherValue.
It is usually a good idea to use hasOwnProperty if you're looping through an object:
for (var key in navigator) {
if (navigator.hasOwnProperty(key) {
var value = navigator[key];
}
}
Hope this helps
The teacher came back with an answer for my for..in array problem:
<script>
var navigatorArray = [];
for (var i in window.navigator) {
navigatorArray.push(window.navigator[i]);
}
navigatorArray.sort();
console.log(navigatorArray);
var htmlString = '';
for (var j = 0; j < navigatorArray.length; j++) {
htmlString += navigatorArray[j] + '<br />';
}
with the .push habbit it should be possible to collect them in an array and index them.
Related
I’m trying to convert text to binary but when my loop runs, it never ends. I cannot figure out why that is so.
Is there a better way to do this?
handleBinaryChange: function(e){
var friendsCopy = this.state.friendsArray;
for (var i = 0; i < friendsCopy.length; i++) {
for (var j = 0; j < friendsCopy[i].friendsName.length; j++) {
console.log(friendsCopy[i].friendsName += friendsCopy[i].friendsName[j].charCodeAt(0).toString(2) + " ");
}//End of 'j' for
}//End of 'i' for
this.setState({
friendsArray: friendsCopy //make friendsCopy contain the new value for friendsName
});
}
}
By using += in friendsCopy[i].friendsName += you are modifying friendsCopy[i].friendsName. On each iteration it gets longer, so it never stops.
If you only want to output it to the console change it to
friendsCopy[i].friendsName + friendsCopy[i].friendsName[j].charCodeAt(0).toString(2) + " ");
You are increasing friendsName value with +=
in each loop iteration
simple solution: use an auxiliary test parameter that stores the starting value:
this way, test value is fixed throughout the entire loop
e.g.:
for(var i=0; i<friendsCopy.length; i++){
var test = friendsCopy[i].friendsName.length; // added this param
for(var j=0; j<test; j++){ // used it here
console.log(friendsCopy[i].friendsName += friendsCopy[i].friendsName[j].charCodeAt(0).toString(2) + " ");
}//End of 'j' for
}//End of 'i' for
You are using the length of friendsName in your break condition, but you keep increasing the length of the string inside the loop:
for(var j=0; j<friendsCopy[i].friendsName.length; j++){
console.log(friendsCopy[i].friendsName += friendsCopy[i].friendsName[j].charCodeAt(0).toString(2) + " ");
}
Note that friendsCopy[i].friendsName.length will be executed for each iteration of the loop, not only once at the beginning.
I need to create an Array asking the user for 5 values to enter.
They must enter city names (strings) and I need to create that using a for loop.
Then I need to output that information using another for loop.
Here is what I have so far:
//Declare the variables
var cities= array(SIZE);
var SIZE = 5;
var index = 0;
var BR = "<br />";
// Create the for loop to prompt the user
for(index = 0; index < SIZE ; index++) {
cities= prompt("Please enter the cities!");
}
//Output the array information
for( /* ? */ ) {
document.write(cities[SIZE]+ " was the city you entered" + BR);
}
I don't know what to put between the for() to output that information. Is there a better way to do this?
The main problem is that you're not adding to the cities - you're replacing the array with a string, each time through the loop. You want to use push() instead.
And don't output cities[SIZE] - that will be past the end of the array. Loop through (just like on input) and output cities[index].
//Declare the variables
var SIZE = 5;
var cities = new Array(SIZE); // JS is case-sensitive
var index = 0;
var BR = "<br />";
//Create the for loop to prompt the user
for (index = 0; index < SIZE; index++) {
cities[index] = prompt("Please enter the cities!");
}
//Output the array information
for (index = 0; index < SIZE; index++) {
document.write(cities[index] + " was the city you entered" + BR);
}
Paul is right. In the first loop you directly set cities variable to the input entered by the user. Also you don't need to set the Array size. Best practice is to use brackets. Shortened version could be:
//Declare the variables
var size = 5,
cities = [];
//Create the for loop to prompt the user
for (var index = 0; index < size; index++) {
cities.push(prompt("Please enter the cities!"));
}
//Output the array information
for (var index in cities) {
document.write(cities[index] + " was the city you entered <br />");
}
I think the code(below) is optimized (just use less variables than my initial version of the same logic).
How do I really know if its properly optimized ?
What factors should I consider during optimization ?
Here is the code (
also on jsfiddle )
function process(arr){
var processed = [];
for(var i=0,len=arr.length;i<len;i++){
if(processed.indexOf(arr[i]) < 0){
var nodes = findIndexes(arr,arr[i]);
if(nodes.length > 1){
for(var j=0,jlen=nodes.length;j<jlen;j++){
arr[nodes[j]] = arr[nodes[j]] + '(' + ( j + 1 ) + ')';
}
}
processed.push(arr[i]);
}
}
return arr;
}
function findIndexes(arr,val){
var node = [];
for(var i=0,len=arr.length;i<len;i++){
if(arr[i] === val){
node.push(i);
}
}
return node;
}
// input
var arr = ['aa','bb','bb','aa','cc','dd','cc','ff']
console.log(process(arr));
//output: ["aa(1)", "bb(1)", "bb(2)", "aa(2)", "cc(1)", "dd", "cc(2)", "ff"]
Here is the explanation of the code. 'process' function looks for the same values inside array and for every same values it changes the value by post pending a number to that values, "number" indicates the count of the value as it found in array.
for example
arr = ["x","x","y","z"] will return ["x(1)","x(2)","y","z"]
"y" and "z" are unchanged because they appeared only once.
To optimize I have used an array named as processed that is used to hold values that are just processed inside main for loop, so in next iterations it can be determined that the new iteration value is already processed or not by checking through the array.indexOf method, if the value is already processed then it can safely skip the underlying logic (if/for statements).
Now I have no idea how to further optimize it other than changing the whole process logic.
Optimizations in a broad sense will involve simplifying code, precomputing results which are repeatedly reused, and organizing code so more results can be reused.
Your fiddle code produced following result on analysis.
Logical LOC: 26
Mean parameter count: 3
Cyclomatic complexity: 7
Cyclomatic complexity density: 27%
Maintainability index: 104
Lines of Code (LOC)– Indicates the approximate number of lines in the code. The count is based on the IL code and is therefore not the exact number of lines in the source code file. A very high count might indicate that a type or method is trying to do too much work and should be split up. It might also indicate that the type or method might be hard to maintain.
Maintainability Index – Calculates an index value between 0 and 100 that represents the relative ease of maintaining the code. A high value means better maintainability. Color coded ratings can be used to quickly identify trouble spots in your code. A green rating is between 20 and 100 and indicates that the code has good maintainability. A yellow rating is between 10 and 19 and indicates that the code is moderately maintainable. A red rating is a rating between 0 and 9 and indicates low maintainability.
Cyclomatic Complexity – Measures the structural complexity of the code. It is created by calculating the number of different code paths in the flow of the program. A program that has complex control flow will require more tests to achieve good code coverage and will be less maintainable.
Check code complexities using online tool for your javascript code.
Reference : Link1,Link 2
Javascript optimiser page
Reference(Provides you with different techniques that you should keep in mind while optimising)
You can do it in a single loop:
function process2(arr) {
var out = arr.slice(0),
seen = {},
len = arr.length,
i, key, item, count;
for (i = 0; i < len; ++i) {
key = out[i];
item = seen[key];
if (!item) {
// firstIndex, count
seen[key] = item = [i, 0];
}
count = ++item[1];
if (count > 1) {
if (count === 2) {
out[item[0]] = key + '(1)';
}
out[i] = key + '(' + count + ')';
}
}
return out;
}
// input
var arr = ['aa', 'bb', 'bb', 'aa', 'cc', 'dd', 'cc', 'ff']
console.time('p2');
console.log(process2(arr));
console.timeEnd('p2');
From benchmarking, process2 is approximately 2x faster than process1. That's just a really naive first pass at the problem.
And yet another way to optimize your code with less changes:
In your specific case you go through the whole array for each new found entry although all previous entries have already been processed so it should be possible to opimize further by passing the current index to findIndexes:
function findIndexes(arr,val, fromIndex){
var node = [];
for(var i=fromIndex,len=arr.length;i<len;i++){
if(arr[i] === val){
node.push(i);
}
}
return node;
}
Currrently your code has a O(n^2) complextity. This is caused by your outer loop of arr in process then a call to findIndexes which again loops through arr.
You can simplify this to an O(n) algorithm that loops through the array twice:
function process(arr) {
var result = [];
var counter = {}, counts = {};
var len = arr.length;
for(var i = 0; i < len; i++){
var value = arr[i];
counter[value] = 1;
counts[value] = (counts[value] || 0) + 1;
}
for(var i = 0; i < len; i++){
var value = arr[i];
if(counts[value] == 1) {
result.push(value);
} else {
result.push(value + "(" + counter[value]++ + ")");
}
}
return result;
}
Here's an example that doesn't use nested loops, and uses an object to store key information:
var obj = {};
// loop over the array storing the elements as keys in the object
// if a duplicate element is found, increment the count value
for (var i = 0, l = arr.length; i < l; i++) {
var key = arr[i];
if (!obj[key]) obj[key] = { count: 0, level: 0 };
obj[key].count++;
}
// remove all the key/values where the count is 1
// ie there are no duplicates
for (var p in obj) {
if (obj[p].count === 1) delete obj[p];
}
// for each element in the original array, increase its 'level'
// amend the element with the count
// reduce the count
for (var i = 0, l = arr.length; i < l; i++) {
var key = arr[i];
if (obj[key] && obj[key].count > 0) {
obj[key].level++;
arr[i] = key + '(' + obj[key].level + ')';
obj[key].count--;
}
}
DEMO
Given this JavaScript array:
var list = [1,1,1,2,2,2,2]
I want to know how I can produce an HTML list below that has each unique item in the array and number of times that they appear in the array. I just want to know the JavaScript to produce the data, I can generate the HTML.
1 is x3
2 is x4
I'm confused about how to achieve this. Basically, similar to shopping cart quantity functionality, but using the array.
http://jsfiddle.net/37ab3k00/
Use .reduce to reduce your array to an object of quantities.
var list = [1, 1, 1, 2, 2, 2, 2];
var quantities = list.reduce(function(obj, n) {
if (obj[n]) obj[n]++;
else obj[n] = 1;
return obj;
}, {});
var ul = document.querySelector("ul");
for (var n in quantities) {
ul.appendChild(document.createElement("li")).textContent = n + " has a quantity x" + quantities[n];
}
<ul></ul>
The first argument to .reduce() is a function that gets invoked for each member in the Array.
The second argument is an object that we're going to pass along to each iteration. It gets passed as the first argument to the function we provided, and we always return it so that it always gets passed as that argument. This is called the accumulator.
The n argument to the function we provided is the value of the current member in the list. So what we do is first see if our obj has a truthy n member. If so, it must have been encountered already, so we increment it. If not, we assign it the initial value of 1 to represent the first n that was found for that value.
var list = [1,1,1,2,2,2,2]
var counts = {};
for (var i = 0; i < list.length; i++) {
counts[list[i]] = 1 + (counts[list[i]] || 0);
}
Demo: http://jsfiddle.net/famn4zcL/2/
Add to HTML
var li = '';
for (var el in counts) {
li += '<li>' + el + ' is x' + counts[el] + '</li>';
}
document.getElementById('list').innerHTML = li;
Demo: http://jsfiddle.net/famn4zcL/3/
Another way would be using array of objects (those can be easily upgraded with additional data that you probably would need building products), like so:
HTML:
<span id="display"></span>
JS (plain, no Framework):
var objects = [
{prod:0,quant:00},
{prod:1,quant:11},
{prod:2,quant:22},
{prod:3,quant:33},
{prod:4,quant:44},
{prod:5,quant:55}
];
var list_of_objects = "", display_id = document.getElementById("display");
for (var key in objects) {
if (objects.hasOwnProperty(key)) {
console.log(key);
list_of_objects += '<li>'+objects[key].prod + ' has a qtty x ' + objects[key].quant+'</li>';
}
}
console.log(list_of_objects);
display_id.innerHTML = list_of_objects;
So you could easily upgrade product data with new info, like:
var objects = [
{prod:0,quant:00,url:"http://url00"},
{prod:1,quant:11,url:"http://url11"},
{prod:2,quant:22,url:"http://url22"},
{prod:3,quant:33,url:"http://url33"},
{prod:4,quant:44,url:"http://url44"},
{prod:5,quant:55,url:"http://url55"}
];
JSfiddle to play with: http://jsfiddle.net/7hokfmdu/
Here is my code so far for my school project (using Murach's JavaScript and DOM Scripting by Ray Harris). The chapter is only about Arrays and does not cover Prototypes, but I wanted to try it out based on Internet tutorials and references:
/*
Operation
This application stores the last name, first name, and score for
one or more students and it calculates the average score for all of the scores
that have been entered. When the user clicks on the Clear button, this
application clears the score data from this application. When the user clicks
on the Sort button, this application sorts the data in alphabetical order by
last name.
Specifications
The program should use one or more arrays to store the data.
Assume that the user will enter valid data.
*/
var $ = function (id)
{
return document.getElementById(id);
}
/*
Array prototype object extension for averaging the contents
"Adding a method to the built-in Array object to extract the average
of any numerical values stored in the array is therefore a useful
addition to that object." http://javascript.about.com/library/blaravg.htm
*/
Array.prototype.average = function ()
{
var avg = 0;
var count = 0;
for (var i = 0; i<this.length; i++)
{
//never gets here:
alert(i + ": " + this[i]);
var e = +this[i];
if(!e && this[i] !== 0 && this[i] !== '0')
{
e--;
}
if (this[i] == e)
{
avg += e;
count++;
}
}
return avg / count;
}
var addScore = function ()
{
studentScores[$('last_name').value + ', ' + $('first_name').value] = $('score').value;
update();
}
var clearScore = function ()
{
for (var i in studentScores)
{
studentScores[i] = '';
}
update();
}
var sortScore = function ()
{
scores.sort();
update();
}
var update = function ()
{
var result = '';
for (var i in studentScores)
{
result += (i + ': ' + studentScores[i] + '\n');
}
$('scores').value = result;
$('average_score').value = studentScores.average().toFixed(1);
}
window.onload = function ()
{
//a variable is initialized inside a function without var, it will have a global scope:
studentScores = [];
$('add_button').onclick = addScore;
$('sort_button').onclick = sortScore;
$('clear_button').onclick = clearScore;
$('last_name').focus();
}
When the code enters the "update()" function (end of the "addScore()" function) and accesses the array,
it populates the "literal" code from the Prototype into the text area (and fails to find the average on the next line):
I don't have enough rep points to post the image, but here is my output (there are no errors in the Chrome JS Console):
lowe, doug: 82
average: function ()
{
var avg = 0;
var count = 0;
for (var i = 0; i<this.length; i++)
{
//never gets here:
alert(i + ": " + this[i]);
var e = +this[i];
if(!e && this[i] !== 0 && this[i] !== '0')
{
e--;
}
if (this[i] == e)
{
avg += e;
count++;
}
}
return avg / count;
}
Any help appreciated (best practice or algorithm suggestions welcome)
Change this:
studentScores = []
to this:
studentScores = {}
...so that you're using an Object instead of an Array.
Your for loop in average() is just iterating numeric indices instead of the non-numeric keys you created.
Create your average() method as a standalone function like the others, and pass studentScores to it to calculate the average, and then use for-in instead of for.
That's simple: Do not use for…in enumerations for looping Arrays! You do so in your clearScore and update functions.
for (var prop in obj) loops over all [enumerable] properties, including those that are inherited from Array.prototype (for Array objects at least). A for (var i=0; i<array.length; i++) loop will not have that problem.
You have to decide whether studentScores is intended to be an array (i.e., an integer is used to access the stored data) or an Object/Associative Array (a string is used to set/get an element).
If you want to use the student's name as the key, you should declare studentScores as an object, and your 'average' method would have to be added to the Object prototype (which I don't recommend).
With the current state of the code, you have stumbled on the fact that an Array is also an object, and can have arbitrary properties attached to it, like any other object. You have added properties by name, but in your average method, you are trying to access numerically based indices. But that's not where the data you're adding is stored.
> a = [];
[]
> a['foo'] = 'bar';
'bar'
> a.length
0
> a[3] = 0;
0
> a.length
4