Javascript code help - Wrong return - javascript

So I'm working on this script. When I'm done with it, it should be used for making 2-and-2 groups. But anyway; The 'input' array in the start of the script, will get 22 different inputs from my HTML file. As a standard, I gave them the values 1-22. The thing is my two blocks '1st number' and '2nd number' doesn't work very well: they don't return the right numbers. Cause I want every elev[x] to be used once! Not 2 times, not 0 times, once! And the blocks returns like some of them twice and some of them isn't even used. So how can I fix this?
function Calculate(){
var elev = [];
var inputs = document.getElementsByName("txt");
for(i=0; i<inputs.length; i++) {
elev[i] = {
"Value": inputs[i].value,
"Used": false
};
}
function shuffle(elev){
var len = elev.length;
for(i=1; i<len; i++){
j = ~~(Math.random()*(i+1));
var temp = elev[i];
arr[i] = elev[j];
arr[j] = temp;
}
}
for(var v=0; v<1; v++) {
shuffle(elev);
document.write(elev + '<br/>\n');
}}
Yes, I'm still new at programming and I just wanna learn what I can learn.
Problem solved by doing the Fisher-Yates shuffle.

The idea of shuffling an array and iterating over it is correct, however, sorting by a coin-flipping comparator (a common mis-implementation; suggested by the other answer) is not the correct way to shuffle an array; it produces very skewed results and is not even guaranteed to finish.
Unless you're willing to fetch underscore.js (arr = _.shuffle(arr)), It is recommended to use the Fisher-Yates shuffle:
function shuffle(arr){
var len = arr.length;
for(var i=1; i<len; i++){
var j = ~~(Math.random()*(i+1)); // ~~ = cast to integer
var temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
...
shuffle(elev);
for(var i=0; i<elev.length; i++){
//do something with elev[i]
console.log(elev[i].Value);
}
also, note that object fields should be lowercase, not uppercase (value', not 'Value'). Also, theUsed` field should not be neccessary since you now iterate in order. Also, any chance you could use an array of Values? They seem to be the only field in your objects.
Also, Don't use document.write(). It doesn't work as expected when used after the page load. If you want to append something to the document, you hate jQuery and other frameworks and you don't want to go the long way of creating and composing DOM nodes, document.body.innerHTML += is still better than document.write() (but still consider appending DocumentFragments instead').
demo: http://jsfiddle.net/honnza/2GSDd/1/

You are only getting random numbers once, and then processing them if they haven't already been used.
I would suggest shuffling the array (elev.sort(function() {return Math.random()-0.5})) and then just iterating through the result without further random numbers.

Related

Two element combinations of an array javascript

If i have an array of letters, for example
['A','C','D','E']
and I wanted to find all the 2 letter combinations of this array, what is the best way of doing this without using 2 for loops. For example:
for (var i=0; i<arr.length;i++) {
for (var j=i+1; j<arr.length;j++) {
console.log(arr[i] + " " + arr[j]);
}
}
The issue with this, is that if the array becomes massive (1000 elements), it usually times out. Is there another way (alternate data structure etc to do this)?
Use the .map()
Something like this
var arr = ['A','C','D','E'],
combinations = arr.map((v,i)=>arr.slice(i+1).map(v2=>v+v2));
console.log(combinations);
Although this code will also iterate twice over the elements.
(it will actually perform worse than your code since map executes a function for each item and it also creates temporary array copies with the slice, so it is here just for an alternate approach, not a more performant.)
Not only two but with any number of elements you might do as follows;
Array.prototype.combinations = function(n){
return this.reduce((p,c,i,a) => p.concat(n > 1 ? a.slice(i+1).combinations(n-1).map(e => [].concat(e,c))
: [[c]]),[]);
};
console.log(JSON.stringify([1,2,3,4,5,6].combinations(2)));
console.log(JSON.stringify([1,2,3,4,5,6].combinations(3)));
Well as per #Lucas Kot-Zaniewski's comments I have refactored my code to use .push() operations in the place of .concat() instructions and also where required a spread operation i did use Array.prototype.push.apply(context,[args]). These two changes made the code to run 2.5 ~ 3 times faster (resulting 3.5-7 msecs vs 9.5-19 msecs) when an input of 100 items array is given and a combination of two of each is requested. Yet once tried with 1000 items of 2 combinations the difference is more dramatic like 400ms vs 6000ms.
A test can be seen at https://repl.it/DyrU
Array.prototype.combinations = function(n){
return this.reduce((p,c,i,a) => (Array.prototype.push.apply(p,n > 1 ? a.slice(i+1).combinations(n-1).map(e => (e.push(c),e))
: [[c]]),p),[]);
};
console.log(JSON.stringify([1,2,3,4,5,6].combinations(2)));
I really beat this one into the ground. As expected, #Louis Durand 's answer of two nested for loops was fastest on an array containing 100 strings (around 4 ms on my machine). This shows that a nested loop is probably your best bet in this situation.
Second fastest is my recursive solution which did the same in around 7-8 ms.
Third was #Redu 's answer which clocked in around 12-15 ms for the same task. I suspect his implementation is slower because he uses slice method in his algo to update array (other answers just increment the index leaving the input array unchanged which is much faster). Also this implementation results in multiple copies of the input array being stored in memory (every time the function is called it creates a new input array from the original array from which it removes the first elelment). This could also potentially affect performance.
So to answer your question: no I don't think there is a much better approach to what you are doing other than concatenating onto string and printing answers at the very end (what Louis suggested).
var arr = [];
for (var i = 0; i< 100; i++){
arr.push(i+"");
}
/*
console.time("test0");
test0();
function test0() {
var s = "";
for (var i=0; i<arr.length-1;i++) {
for (var j=i+1; j<arr.length;j++) {
s += arr[i] + " " + arr[j]+" ; ";
}
s += "\n";
}
console.log(s);
}
console.timeEnd("test0");
*/
console.time("test1");
test1();
function test1() {
var output = [];
getCombos(0, 0, [], 2);
console.log(JSON.stringify(output));
function getCombos(index, depth, tmp, k){
if(depth < k){
for(var i = index; i<arr.length; i++){
var tmp1 = [arr[i]];
Array.prototype.push.apply(tmp1, tmp);
getCombos(i+1, depth+1,tmp1, k);
}
}else{
output.push(tmp);
}
}
}
console.timeEnd("test1");
/*
console.time("test2");
test2();
function test2(){
Array.prototype.combinations = function(n){
return this.reduce((p,c,i,a) => (Array.prototype.push.apply(p,n > 1 ? a.slice(i+1).combinations(n-1).map(e => (e.push(c),e))
: [[c]]),p),[]);
};
console.log(JSON.stringify(arr.combinations(2)));
}
console.timeEnd("test2");*/
Here is a recursive solution that doesn't solve your time complexity problem but is another way to consider. The added advantage is that you can generalize it to any k so you are not stuck finding only combinations of two letters. Also you only declare one loop (although more than one copy of it will exist in your call stack)
var arr = ["a", "b", "c", "d", "e"];
var output = "";
getCombos(0, 0, [], 2);
console.log(output);
function getCombos(index, depth, tmp, k){
if(depth < k){
for(var i = index; i<arr.length; i++){
var tmp1 = [...tmp, arr[i]];
getCombos(i+1, depth+1,tmp1, k);
}
}else{
output += tmp.toString() + ";";
}
}
I think your code has a mistake cause here, E would never be used.
It should rather be:
var s = "";
for (var i=0; i<arr.length-1;i++) {
for (var j=i+1; j<arr.length;j++) {
s += arr[i] + " " + arr[j]+" ; ";
}
s += "\n";
}
console.log(s);
Note that if you log everything in the console, it's no surprise that it times out.

JavaScript For Loop Keeps Looping Infinity

I've written the functions below as part of a much larger application for processing FASTA formatted files via a web interface. For some reason it decided to run into infinity when call upon my baseCounts() function from within makePretty(). It might be worth noting that both functions are encapsulated by the same parent function.
The function baseCounts() returns valid data in the form of a 100+ long array, console.log confirms that it is not to blame so the problem has to be with makePretty().
Any help is welcome.
function baseCount(records){
// Count instances of Bases in array
var basecounts = Array();
for (i=0; i < records.length; i++){
var record = records[i];
console.log(record);
var count = [record.match(/A/g), record.match(/T/g), record.match(/C/g), record.match(/G/g)];
var basecount = Array();
for (i=0; i < count.length; i++){
basecount.push(count[i].length);
}
// return array of occurance
basecounts.push(basecount);
}
}
function makePretty(fasta){
// Make FASTA more human friendly
var data = Array();
var basecounts = Array();
var bases = Array();
console.log(fasta.length);
// Generate base array
for (i=1; i < fasta.length; i++){
bases.push(fasta[i][2])
}
basecounts = baseCount(bases); // RUNS INTO INFINITY
for (i=0; i < fasta.length; i++){
var record = Array();
record.push(i); // Add protein number
record.push(fasta[i][0]); // Add NC_#
record.push(fasta[i][1]); // Add base index
_record = fasta[i][2];
var l_record = _fasta.length; // Protein length
//var basecount = baseCount(_record);
var cg_content;
}
}
Your nested loops are using the same variable i, and clobbering each other's state.
for (i=0; i < records.length; i++){
...
for (i=0; i < count.length; i++){
...
}
Use distinct variables, say i and j or better yet pick meaningful names.
Also you should declare the variables (var i) to ensure they're local to the function.
Finally, use ++i, not i++. The former means "increment i" while the latter means "i, and oh by the way increment it". They both increment i, but the latter one returns the old value, which is a special language feature to use in special cases.
You're reseting your variable counter in your inner loop (i).
To avoid this, and future problems like it as well as hoisting issues, I would suggest using the newer functions such as forEach or map. You can also clean up your code this way:
function baseCountFunc(records){
// Count instances of Bases in array
var basecount = [];
records.forEach(function(record) {
var count = [record.match(/A/g), record.match(/T/g), record.match(/C/g), record.match(/G/g)];
count.forEach(function(countElement) {
basecount.push(countElement.length);
});
basecounts.push(basecount);
});
}
Also, I noticed you named your function the same name as your variables, you should avoid that as well.

Optimizing JavaScript autocorrect implementation

I modified the jQuery Autocomplete implementation to generate autocorrect suggestions too. I used Levenshtein distance as the metric to decide closest matches.
My code runs at each keypress after the jQuery autocomplete does not have any more suggestions. Here's the code that I wrote:
// Returns edit distance between two strings
edit_distance : function(s1, s2) {
// Auxiliary 2D array
var arr = new Array(s1.length+1);
for(var i=0 ; i<s1.length+1 ; i++)
arr[i] = new Array(s2.length+1);
// Algorithm
for(var i=0 ; i<=s1.length ; i++)
for(var j=0 ; j<=s2.length ; j++)
arr[i][j] = 0;
for(var i=0 ; i<=s1.length ; i++)
arr[i][0] = i;
for(var i=0 ; i<=s2.length ; i++)
arr[0][i] = i;
for(var i=1 ; i<=s1.length ; i++)
for(var j=1 ; j<=s2.length ; j++)
arr[i][j] = Math.min(arr[i-1][j-1] + (s1.charAt(i-1)==s2.charAt(j-1) ? 0 : 1), arr[i-1][j]+1, arr[i][j-1]+1);
// Final answer
return arr[s1.length][s2.length].toString(10);
},
// This is called at each keypress
auto_correct : function() {
// Make object array for sorting both names and IDs in one go
var objArray = new Array();
for(var i=0 ; i<idArray.length ; i++) {
objArray[i] = new Object();
objArray[i].id = idArray[i];
objArray[i].name = nameArray[i];
}
// Sort object array by edit distance
var out = this;
companyObjArray.sort (
function(a,b) {
var input = jQuery("#inputbox").val().toLowerCase();
var d1 = a.name.toLowerCase();
var d2 = b.name.toLowerCase();
return out.editDistance(input,d1) - out.editDistance(input,d2);
}
);
// Copy some closest matches in arrays that are shown by jQuery
this.suggestions = new Array();
this.data = new Array();
for(var i=0 ; i<5 ; i++) {
this.suggestions.push(companyObjArray[i].name);
this.data.push(companyObjArray[i].id);
}
}
All names have IDs associated with them, so before sorting I'm just creating an object array out of them, and sorting the array.
Since the list to search from is in thousands, its slow. I found a data structure called a BK-tree, which can speed it up, but I'm can't implement it right now. I'm looking for optimization suggestions to speed this up. Any suggestions are welcome. Thanks in advance.
EDIT. I decided to use Sift3 as my string distance metric instead of Levenshein distance, it gives more meaningful results and is faster.
There are quite a few things you can optimize here, but most of it boils down to this: Only do each of your 'heavier' calculations once. You are doing a lot of work on each keypress, in every sort comparison, etc., and caching those values will help a lot.
However, for a significant added performance boost, there is another, quite nifty optimization trick you can use. It is used by every major search engine, including on-site search engines on sites such as Amazon.com.
It takes advantage of the fact that you really don't need to sort the whole list, since all you're displaying are the top 10 or 12 items. Whether the rest of the thousands of list items are in the correct order doesn't matter in the least. So all you really have to keep track of while running through the list, are the top 10 or 12 items you've seen so far, which, as it turns out, is a lot faster than full sorting.
Here's the idea in pseudocode:
The user types a character, creating a new search term
We define an empty shortlist array of length 12 (or however many suggestions we want)
Loop through the full list of words (we will call it the dictionary):
Calculate the (Levenshtein) edit distance between the dictionary word and the search term
If the distance is lower (better) than the current threshold, we:
Add the word to the top of the shortlist
Let the bottom word in the shortlist 'overflow' out of the list
Set our threshold distance to match the word now at the bottom of the shortlist
When the loop has finished, we have a shortlist containing some of the best words, but they are unsorted, so we sort just those 12 words, which will be really fast.
One small caveat: Depending on the dataset and the number of elements in your shortlist, the shortlist may differ slightly from the actual top elements, but this can be alleviated by increasing the shortlist size, e.g. to 50, and just removing the bottom 38 once the final sort is done.
As for the actual code:
// Cache important elements and values
var $searchField = $('#searchField'),
$results = $('#results'),
numberOfSuggestions = 12,
shortlistWindowSize = 50,
shortlist,
thresholdDistance;
// Do as little as possible in the keyboard event handler
$searchField.on('keyup', function(){
shortlist = [];
thresholdDistance = 100;
// Run through the full dictionary just once,
// storing just the best N we've seen so far
for (var i=0; i<dictionarySize; i++) {
var dist = edit_distance(this.value, dictionary[i]);
if (dist < thresholdDistance) {
shortlist.unshift({
word: dictionary[i],
distance: dist
});
if (shortlist.length > shortlistWindowSize) {
shortlist.pop();
thresholdDistance = shortlist[shortlistWindowSize-1].distance;
}
}
}
// Do a final sorting of just the top words
shortlist.sort(function(a,b){
return a.distance - b.distance;
});
// Finally, format and show the suggestions to the user
$results.html('<p>' + $.map(shortlist, function(el){
return '<span>[dist=' + el.distance + ']</span> ' + el.word;
}).slice(0,numberOfSuggestions).join('</p><p>') + '</p>').show();
});
Try the method out in this 12.000 word demo on jsFiddle

Can I select 2nd element of a 2 dimensional array by value of the first element in Javascript?

I have a JSON response like this:
var errorLog = "[[\"comp\",\"Please add company name!\"],
[\"zip\",\"Please add zip code!\"],
...
Which I'm deserializing like this:
var log = jQuery.parseJSON(errorLog);
Now I can access elements like this:
log[1][1] > "Please add company name"
Question:
If I have the first value comp, is there a way to directly get the 2nd value by doing:
log[comp][1]
without looping through the whole array.
Thanks for help!
No. Unless the 'value' of the first array (maybe I should say, the first dimension, or the first row), is also it's key. That is, unless it is something like this:
log = {
'comp': 'Please add a company name'
.
.
.
}
Now, log['comp'] or log.comp is legal.
There are two was to do this, but neither avoids a loop. The first is to loop through the array each time you access the items:
var val = '';
for (var i = 0; i < errorLog.length; i++) {
if (errorLog[i][0] === "comp") {
val = errorLog[i][1];
break;
}
}
The other would be to work your array into an object and access it with object notation.
var errors = {};
for (var i = 0; i < errorLog.length; i++) {
errors[errorLog[i][0]] = errorLog[i][1];
}
You could then access the relevant value with errors.comp.
If you're only looking once, the first option is probably better. If you may look more than once, it's probably best to use the second system since (a) you only need to do the loop once, which is more efficient, (b) you don't repeat yourself with the looping code, (c) it's immediately obvious what you're trying to do.
No matter what you are going to loop through the array somehow even it is obscured for you a bit by tools like jQuery.
You could create an object from the array as has been suggested like this:
var objLookup = function(arr, search) {
var o = {}, i, l, first, second;
for (i=0, l=arr.length; i<l; i++) {
first = arr[i][0]; // These variables are for convenience and readability.
second = arr[i][1]; // The function could be rewritten without them.
o[first] = second;
}
return o[search];
}
But the faster solution would be to just loop through the array and return the value as soon as it is found:
var indexLookup = function(arr, search){
var index = -1, i, l;
for (i = 0, l = arr.length; i<l; i++) {
if (arr[i][0] === search) return arr[i][1];
}
return undefined;
}
You could then just use these functions like this in your code so that you don't have to have the looping in the middle of all your code:
var log = [
["comp","Please add company name!"],
["zip","Please add zip code!"]
];
objLookup(log, "zip"); // Please add zip code!
indexLookup(log, "comp"); // Please add company name!
Here is a jsfiddle that shows these in use.
Have you looked at jQuery's grep or inArray method?
See this discussion
Are there any jquery features to query multi-dimensional arrays in a similar fashion to the DOM?

using javascript shuffled integers to populate sequential html areas

I'm trying to change the contents of sequential divs with randomly-ordered contents. It's not working so well.
I have about 25 <div> tags that, because of other javascripts regulating touch behaviour, are unable to be put in a randomised order using the usual methods. So instead, I'm trying to have the contents of those <div>s randomised by setting different innerHTML values when the document is loaded.
I have a script that provides random integers as I need, but then I'm having trouble getting it to populate the ordered divs.
Here's what I'm using that isn't working:
ArrayList<Integer> list = new ArrayList<Integer>(25);
for(int i = 0; i < 25; i++)
{
list.add(i);
}
Collections.shuffle(list);
Object[] randomNumbers = (Object[])list.toArray();
function testit() {
for(int i = 0; i < 25; i++)
{
var a = document.getElementById('if'+i);
a.innerHTML = randomNumbers[i];
}
}
What I'd like to see is that this takes all my <div>s which have id's such as "if1" "if2" and so one and then set the content of that <div> with the randomised numbers.
I know document.getElementById('if'+i) is surely the wrong way to get ID "if1" then "if2", but I'm not really sure how else it can be done.
Basically I just need a way to populate 25 absolutely-positions <div>s with 25 chunks of HTML content but in a random order. I'd think that could be done by pairing sequential numbers to randomly ordered numbers and tying those into <div> id's. Normally I'd just do this with PHP and be done already but I need it to work as an offline mobile webapp so I'm trying to do it with JS and CSS. I'm definitely open to other approaches.
edit:
If the code above is Java and not Javascript, that's the problem. Then I guess I'm looking for alternative ways, in Javascript, to produce random, non-repeating integers and link those to sequential integers, all between 1 and 25
Here is a sample implementation using javascript:
function shuffle(arr){
//simple shuffle algorithm - you can also use other
//we will get simple base index as basis for swapping elements
//we will swap the array elements arr.length times
var index = Math.floor(Math.random() * arr.length);
for(var i=1;i<arr.length; i++) {
var t = arr[i];
arr[i] = arr[index];
arr[index] = t;
}
return arr;
}
//create an array
var numbers =[];
for(var i=0;i<10;i++)
numbers.push(i);
//call the shuffle function
//call this many times to reshuffle elements
numbers = shuffle(numbers);
//document.write(numbers.join(','));
//test
for(var i=0;i<numbers.length;i++)
{
var a = document.getElementById('if'+i);
a.innerHTML = numbers[i];
}

Categories