Why is `appendChild()` giving me issues? - javascript
H~ I have a script that I've been working on which will, ultimately, display Pascal's Triangle. I developed this from scratch with JavaScript, and I want to display it in the DOM.
For the life of me, I can't figure out why this script produces a different effect in the DOM than it does in a console. Any help would be much appreciated!
Copyright (c) 2015 Peter Gray Ward
function randomScripts(){
var arr1 = ['_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_'];
var arr2 = ['_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_'];
var n = 0;
var len = 35;
var mid = Math.floor(len/2)
var destination = document.getElementById("destination");
var home = document.getElementById("home");
function first(){
for(var j = 1; j<=10; j++){
var o = j%2 !== 0;
var e = j%2 === 0
if(o){
for(var i = 0; i<len; i++){
odd = i%2 !== 0;
if(j === 1){
arr1.splice(i,1,"_")
}
if(odd && (i === mid + n || i === mid - n)){
arr1.splice(i,1,1);
}
var node = document.createTextNode(arr1.join(''));
}
console.log(arr1.join(''));
destination.appendChild(node);
home.appendChild(node);
//destination.appendChild("Please see Console for full version b/c of hackers");
}
else if(e){
for(var h = 0; h<len; h++){
even = h%2 === 0;
if(even && (h === mid + n || h === mid - n)){
arr2.splice(h,1,n);
}
}
console.log(arr2.join(''));
}
n++;
}
}
<p id="destination"></p>
You created functions, but you never call them. Try actually calling them to see what they do:
function randomScripts(){
var arr1 = ['_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_'];
var arr2 = ['_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_'];
var n = 0;
var len = 35;
var mid = Math.floor(len/2)
var destination = document.getElementById("destination");
var home = document.getElementById("home");
first();
function first(){
for(var j = 1; j<=10; j++){
var o = j%2 !== 0;
var e = j%2 === 0
if(o){
for(var i = 0; i<len; i++){
odd = i%2 !== 0;
if(j === 1){
arr1.splice(i,1,"_")
}
if(odd && (i === mid + n || i === mid - n)){
arr1.splice(i,1,1);
}
var node = document.createElement('p');
node.textContent = arr1.join('');
}
console.log(arr1.join(''));
destination.appendChild(node);
home.appendChild(node);
destination.innerHTML = "Please see Console for full version b/c of hackers";
}
else if(e){
for(var h = 0; h<len; h++){
even = h%2 === 0;
if(even && (h === mid + n || h === mid - n)){
arr2.splice(h,1,n);
}
}
console.log(arr2.join(''));
}
n++;
}
}
}
window.addEventListener('load', randomScripts, false);
<p id="destination"></p>
<div id="home"></div>
There may be other problems in the script, but here are a couple I noticed. First:
destination.appendChild(node);
home.appendChild(node);
You can't append the same node in two different places in the DOM. It will remove the first one when you append it the second time. You could either create a second text node the same way you created the first, or clone the node.
Also, these two lines of code come after the if(o) statement body, but the node variable is created inside that if body. In the general case, you wouldn't expect this to work always, because node would be undefined if the if body were not executed. In your specific code, that won't happen because o is true the first time through the loop. But node only gets updated on every second pass through the loop. Is that what you want?
One last thing, the indentation is not consistent, making it hard to see what is nested inside what. It's important to use correct and consistent indentation. (And I would suggest 4 spaces instead of 2 to make it easier for people to read.)
Related
Sequential strings javascript
I'm stuck on sequential string generation, the last iteration of the code below, is trying to return ac. var charcode = 'abcdefghijklmnopqrstuvwxyz0123456789_'; var arr = []; var len = 1; var current = 0; for(var i = 0; i < 40; i++) { if(current == charcode.length) { current = 0 len++ } var tmpStr = '' for(var l = 0; l < len; l++) { tmpStr = tmpStr + charcode[current]; } console.log(tmpStr) current++ } However, it produces cc. Sorry, somehow deleted the intended output a, b, c ... aa, ab, ac ... ba, bc, bb ... ∞ My current understanding suggests within the l < len loop I should check if len > 1 and if so break out and loop for the current charcode[x] but I just can't wrap my head around breaking in and out as it grows.
You can do it like this: var charcode = 'abcdefghijklmnopqrstuvwxyz0123456789_'; var arr = []; var len = 0; var current = 0; for(var i = 0; i < 40; i++) { if(current == charcode.length) { current = 0; len++; } var tmpStr = (len===0) ? '' : charcode[len-1]; tmpStr = tmpStr + charcode[current]; current++; console.log(tmpStr); } In brief, you didn't need a second loop, but an iteration instead (which is achieved through charcode[len-1]). JS Bin here. UPDATE If you need a continuos loop until the very end of the charset, you can use this: This one introduces a third variable (digits) to the iteration and choses a .substr() instead of a single character out of the charset, so that everytime the end of the charset is reached, another digit will be added. The variable called x sets the limit. I have tried 4000 and it looks like it is working. var charcode = 'abcdefghijklmnopqrstuvwxyz0123456789_', arr = [], len = 0, digits = 2, current = 0, tmpStr = ''; var x = 4000; for(var i=0; i<x; i++) { if(current === charcode.length) { current = 0; len++; if(tmpStr.substr(0,1) === charcode.substr(-digits,1) || tmpStr === charcode.substr(-1) + '' + charcode.substr(-1)) { len = 1; digits++; } } if(digits === charcode.length) { console.log(charcode); console.log(i + ' results found'); break; } tmpStr = (len===0) ? '' : charcode.substr([len-1], digits-1); tmpStr += charcode[current]; current++; console.log(tmpStr); } Final JS Bin here.
Unless I'm failing to understand your question, which is highly likely, functional programming solves this problem in an incredibly easy way const stringSequence = xs => flatMap (x => map (add (x)) (xs)) (xs); let chars = 'abcdefghijklmnopqrstuvwxyz0123456789_' console.log (stringSequence (chars)); // ["aa","ab","ac","ad","ae","af","ag","ah","ai","aj","ak","al","am","an","ao","ap","aq","ar","as","at","au","av","aw","ax","ay","az","a0","a1","a2","a3","a4","a5","a6","a7","a8","a9","a_","ba","bb","bc","bd","be","bf","bg","bh","bi","bj","bk","bl","bm","bn","bo","bp","bq","br","bs","bt","bu","bv","bw","bx","by","bz","b0","b1","b2","b3","b4","b5","b6","b7","b8","b9","b_","ca","cb","cc","cd","ce","cf","cg","ch","ci","cj","ck","cl","cm","cn","co","cp","cq","cr","cs","ct","cu","cv","cw","cx","cy","cz","c0","c1","c2","c3","c4","c5","c6","c7","c8","c9","c_","da","db","dc","dd","de","df","dg","dh","di","dj","dk","dl","dm","dn","do","dp","dq","dr","ds","dt","du","dv","dw","dx","dy","dz","d0","d1","d2","d3","d4","d5","d6","d7","d8","d9","d_","ea","eb","ec","ed","ee","ef","eg","eh","ei","ej","ek","el","em","en","eo","ep","eq","er","es","et","eu","ev","ew","ex","ey","ez","e0","e1","e2","e3","e4","e5","e6","e7","e8","e9","e_","fa","fb","fc","fd","fe","ff","fg","fh","fi","fj","fk","fl","fm","fn","fo","fp","fq","fr","fs","ft","fu","fv","fw","fx","fy","fz","f0","f1","f2","f3","f4","f5","f6","f7","f8","f9","f_","ga","gb","gc","gd","ge","gf","gg","gh","gi","gj","gk","gl","gm","gn","go","gp","gq","gr","gs","gt","gu","gv","gw","gx","gy","gz","g0","g1","g2","g3","g4","g5","g6","g7","g8","g9","g_","ha","hb","hc","hd","he","hf","hg","hh","hi","hj","hk","hl","hm","hn","ho","hp","hq","hr","hs","ht","hu","hv","hw","hx","hy","hz","h0","h1","h2","h3","h4","h5","h6","h7","h8","h9","h_","ia","ib","ic","id","ie","if","ig","ih","ii","ij","ik","il","im","in","io","ip","iq","ir","is","it","iu","iv","iw","ix","iy","iz","i0","i1","i2","i3","i4","i5","i6","i7","i8","i9","i_","ja","jb","jc","jd","je","jf","jg","jh","ji","jj","jk","jl","jm","jn","jo","jp","jq","jr","js","jt","ju","jv","jw","jx","jy","jz","j0","j1","j2","j3","j4","j5","j6","j7","j8","j9","j_","ka","kb","kc","kd","ke","kf","kg","kh","ki","kj","kk","kl","km","kn","ko","kp","kq","kr","ks","kt","ku","kv","kw","kx","ky","kz","k0","k1","k2","k3","k4","k5","k6","k7","k8","k9","k_","la","lb","lc","ld","le","lf","lg","lh","li","lj","lk","ll","lm","ln","lo","lp","lq","lr","ls","lt","lu","lv","lw","lx","ly","lz","l0","l1","l2","l3","l4","l5","l6","l7","l8","l9","l_","ma","mb","mc","md","me","mf","mg","mh","mi","mj","mk","ml","mm","mn","mo","mp","mq","mr","ms","mt","mu","mv","mw","mx","my","mz","m0","m1","m2","m3","m4","m5","m6","m7","m8","m9","m_","na","nb","nc","nd","ne","nf","ng","nh","ni","nj","nk","nl","nm","nn","no","np","nq","nr","ns","nt","nu","nv","nw","nx","ny","nz","n0","n1","n2","n3","n4","n5","n6","n7","n8","n9","n_","oa","ob","oc","od","oe","of","og","oh","oi","oj","ok","ol","om","on","oo","op","oq","or","os","ot","ou","ov","ow","ox","oy","oz","o0","o1","o2","o3","o4","o5","o6","o7","o8","o9","o_","pa","pb","pc","pd","pe","pf","pg","ph","pi","pj","pk","pl","pm","pn","po","pp","pq","pr","ps","pt","pu","pv","pw","px","py","pz","p0","p1","p2","p3","p4","p5","p6","p7","p8","p9","p_","qa","qb","qc","qd","qe","qf","qg","qh","qi","qj","qk","ql","qm","qn","qo","qp","qq","qr","qs","qt","qu","qv","qw","qx","qy","qz","q0","q1","q2","q3","q4","q5","q6","q7","q8","q9","q_","ra","rb","rc","rd","re","rf","rg","rh","ri","rj","rk","rl","rm","rn","ro","rp","rq","rr","rs","rt","ru","rv","rw","rx","ry","rz","r0","r1","r2","r3","r4","r5","r6","r7","r8","r9","r_","sa","sb","sc","sd","se","sf","sg","sh","si","sj","sk","sl","sm","sn","so","sp","sq","sr","ss","st","su","sv","sw","sx","sy","sz","s0","s1","s2","s3","s4","s5","s6","s7","s8","s9","s_","ta","tb","tc","td","te","tf","tg","th","ti","tj","tk","tl","tm","tn","to","tp","tq","tr","ts","tt","tu","tv","tw","tx","ty","tz","t0","t1","t2","t3","t4","t5","t6","t7","t8","t9","t_","ua","ub","uc","ud","ue","uf","ug","uh","ui","uj","uk","ul","um","un","uo","up","uq","ur","us","ut","uu","uv","uw","ux","uy","uz","u0","u1","u2","u3","u4","u5","u6","u7","u8","u9","u_","va","vb","vc","vd","ve","vf","vg","vh","vi","vj","vk","vl","vm","vn","vo","vp","vq","vr","vs","vt","vu","vv","vw","vx","vy","vz","v0","v1","v2","v3","v4","v5","v6","v7","v8","v9","v_","wa","wb","wc","wd","we","wf","wg","wh","wi","wj","wk","wl","wm","wn","wo","wp","wq","wr","ws","wt","wu","wv","ww","wx","wy","wz","w0","w1","w2","w3","w4","w5","w6","w7","w8","w9","w_","xa","xb","xc","xd","xe","xf","xg","xh","xi","xj","xk","xl","xm","xn","xo","xp","xq","xr","xs","xt","xu","xv","xw","xx","xy","xz","x0","x1","x2","x3","x4","x5","x6","x7","x8","x9","x_","ya","yb","yc","yd","ye","yf","yg","yh","yi","yj","yk","yl","ym","yn","yo","yp","yq","yr","ys","yt","yu","yv","yw","yx","yy","yz","y0","y1","y2","y3","y4","y5","y6","y7","y8","y9","y_","za","zb","zc","zd","ze","zf","zg","zh","zi","zj","zk","zl","zm","zn","zo","zp","zq","zr","zs","zt","zu","zv","zw","zx","zy","zz","z0","z1","z2","z3","z4","z5","z6","z7","z8","z9","z_","0a","0b","0c","0d","0e","0f","0g","0h","0i","0j","0k","0l","0m","0n","0o","0p","0q","0r","0s","0t","0u","0v","0w","0x","0y","0z","00","01","02","03","04","05","06","07","08","09","0_","1a","1b","1c","1d","1e","1f","1g","1h","1i","1j","1k","1l","1m","1n","1o","1p","1q","1r","1s","1t","1u","1v","1w","1x","1y","1z","10","11","12","13","14","15","16","17","18","19","1_","2a","2b","2c","2d","2e","2f","2g","2h","2i","2j","2k","2l","2m","2n","2o","2p","2q","2r","2s","2t","2u","2v","2w","2x","2y","2z","20","21","22","23","24","25","26","27","28","29","2_","3a","3b","3c","3d","3e","3f","3g","3h","3i","3j","3k","3l","3m","3n","3o","3p","3q","3r","3s","3t","3u","3v","3w","3x","3y","3z","30","31","32","33","34","35","36","37","38","39","3_","4a","4b","4c","4d","4e","4f","4g","4h","4i","4j","4k","4l","4m","4n","4o","4p","4q","4r","4s","4t","4u","4v","4w","4x","4y","4z","40","41","42","43","44","45","46","47","48","49","4_","5a","5b","5c","5d","5e","5f","5g","5h","5i","5j","5k","5l","5m","5n","5o","5p","5q","5r","5s","5t","5u","5v","5w","5x","5y","5z","50","51","52","53","54","55","56","57","58","59","5_","6a","6b","6c","6d","6e","6f","6g","6h","6i","6j","6k","6l","6m","6n","6o","6p","6q","6r","6s","6t","6u","6v","6w","6x","6y","6z","60","61","62","63","64","65","66","67","68","69","6_","7a","7b","7c","7d","7e","7f","7g","7h","7i","7j","7k","7l","7m","7n","7o","7p","7q","7r","7s","7t","7u","7v","7w","7x","7y","7z","70","71","72","73","74","75","76","77","78","79","7_","8a","8b","8c","8d","8e","8f","8g","8h","8i","8j","8k","8l","8m","8n","8o","8p","8q","8r","8s","8t","8u","8v","8w","8x","8y","8z","80","81","82","83","84","85","86","87","88","89","8_","9a","9b","9c","9d","9e","9f","9g","9h","9i","9j","9k","9l","9m","9n","9o","9p","9q","9r","9s","9t","9u","9v","9w","9x","9y","9z","90","91","92","93","94","95","96","97","98","99","9_","_a","_b","_c","_d","_e","_f","_g","_h","_i","_j","_k","_l","_m","_n","_o","_p","_q","_r","_s","_t","_u","_v","_w","_x","_y","_z","_0","_1","_2","_3","_4","_5","_6","_7","_8","_9","__"] Oh but you don't have flatMap? Easy, just make your own, as well as any other simple procedures that describe the computations you would like to perform const add = x => y => x + y; const map = f => xs => Array.prototype.map.call(xs, x => f (x)); const reduce = f => i => xs => Array.prototype.reduce.call(xs, (x,y) => f (x) (y)); const concat = xs => x => xs.concat(x); const flatMap = f => xs => reduce (concat) ([]) (map (f) (xs)); These procedures are highly reusable and free you from the shackles of imperative code that looks like this var charcode = 'abcdefghijklmnopqrstuvwxyz0123456789_'; // setup several state variables var arr = []; var len = 1; // 6 months later, why did we initialize len at 1? var current = 0; // manual looping, with arbitrary static input of 40 (why 40?) for(var i = 0; i < 40; i++) { // logic with potential for human error if(current == charcode.length) { // variable mutations current = 0 len++ } // temporary variables var tmpStr = '' // move manual looping for(var l = 0; l < len; l++) { // more variable mutation tmpStr = tmpStr + charcode[current]; } // IO side effects console.log(tmpStr) // manual iteration current++ }
JavaScript procedure to find the characters that are in one string but not in another
I'e been trying to write one and it's getting messy! Suppose I have two strings textStart, textTarget and I want to keep track of the characters I would need to add and remove from textStart in order to product textTarget. For instance, if textStart = "dude" and textTarget = "deck", then characters that would need to be added would be 'c' and 'k' and the characters that would need to be substracted would be the 'u' and one of the 'd's. I'm thinking that I first need to create maps that represent the number of each character in textStart and textTarget. So I wrote this: var startChars = {}; for (var k = 0, n = textStart.length; k < n; ++k) { if (textStart[k] in startChars) ++startChars[textStart[k]]; else startChars[textStart[k]] = 1; } var targetChars = {}; for (var k = 0, n = textTarget.length; k < n; ++k) { if (textTarget[k] in startChars) ++targetChars[textTarget[k]]; else map1[targetChars[k]] = 1; } Which would give me startChars['d']=2, startChars['u']=1, startChars['e']=1 and targetChars['d']=1, targetChars['e']=1, targetChars['c']=1, targetChars['k']=1 Then I can make create maps needAdded and needRemoved that look at the difference in the above two maps: var needAdded = {}; var needRemoved = {}; I'm not sure how to fill those maps as intended, because I don't know how to iterate through the keys of a map using JavaScript. I somehow need to end up with needAdded['c']=1, needAdded['k']=1, needRemoved['u']=1, needRemoved['d']=1 That's where you guys come in and help me. I hope I've done a good job describing what I'm trying to do and how I've tried to do it so far. My programming intuition tells me that I'm writing too many lines of code and that I need to consult StackOverflow for help. Any way to do this elegantly without JQuery or Regex? I know someone's going to come in this thread and write a 1-line Regex solution or something like that.
var s = 'dude', t = 'deck', finalOutput = ''; for (var i = 0; i < s.length; i++){ if ( typeof t[i] != 'undefined' ){ if ( s[i] != t[i] ){ console.log(s[i] + ' changed to ' + t[i]); s[i] = t[i]; finalOutput += t[i]; } else{ finalOutput += s[i]; } } } console.log('FINAL: ' + finalOutput);
Here's a jsfiddle I just spent way too much time on... hopefully it makes sense :) var textStart = 'dude'; var textTarget = 'deck'; var startChars = {}; for (var k = 0, n = textStart.length; k < n; ++k) { if (textStart[k] in startChars) ++startChars[textStart[k]]; else startChars[textStart[k]] = 1; } var targetChars = {}; for (var k = 0, n = textTarget.length; k < n; ++k) { if (textTarget[k] in targetChars) ++targetChars[textTarget[k]]; else targetChars[textTarget[k]] = 1; } console.log('start: ' + JSON.stringify(startChars)); console.log('target: ' + JSON.stringify(targetChars)); var needAdded = {}; var needRemoved = {}; for (var c in startChars) { // If target does not contain letter, remove all, otherwise remove excess if (targetChars[c] > 0) { if (startChars[c] > targetChars[c]) needRemoved[c] = startChars[c] - targetChars[c]; else if (startChars[c] < targetChars[c]) needAdded[c] = targetChars[c] - startChars[c]; } else { needRemoved[c] = startChars[c]; } } for (var c in targetChars) { // If start does not contain letter, add all, otherwise add excess if (startChars[c] > 0) { if (startChars[c] > targetChars[c]) needRemoved[c] = startChars[c] - targetChars[c]; else if (startChars[c] < targetChars[c]) needAdded[c] = targetChars[c] - startChars[c]; } else { needAdded[c] = targetChars[c]; } } console.log('needAdded: ' + JSON.stringify(needAdded)); console.log('needRemoved: ' + JSON.stringify(needRemoved)); The output is as follows: start: {"d":2,"u":1,"e":1} target: {"d":1,"e":1,"c":1,"k":1} needAdded: {"c":1,"k":1} needRemoved: {"d":1,"u":1}
Ok, also too much time on this: var textStart = "dude"; var textTarget = "duck"; var map = {}; MapCharacters(textStart, map, 1); MapCharacters(textTarget, map, -1); console.log(map); var toDelete = []; var toAdd = []; for (var prop in map) { if (map.hasOwnProperty(prop)) { while (map[prop] > 0) { toDelete.push(prop); map[prop]--; } while (map[prop] < 0) { toAdd.push(prop); map[prop]++; } } } console.log(toDelete); console.log(toAdd); function MapCharacters(string, map, add) { for (var k = 0, n = string.length; k < n; ++k) { if (string[k] in map) { map[string[k]] += add; } else { map[string[k]] = add; } } } http://jsfiddle.net/nSV2J/1/ It could probably be done more efficiently, but as I said - too much time! I realized that the best way to do this is not to make two maps, but just one. In the first case you increment the count for each letter and in the second case you decrease it. Now it's easy to find which ones need to be removed (the ones that end up > 0) and which ones need to be added (the ones that end up < 0)
Most efficient way to merge two arrays of objects
I've already solved this out. However I'm looking for a faster solution since my variables has thousands of objects. I have two arrays like this: var full = [{a:'aa1',b:'bb1'},{a:'aa3',b:'bb2'},{a:'aa3',b:'bb3'},{a:'aa2',b:'bb3'}], some = [{a:'aa1',b:'bb1'},{a:'aa3',b:'bb3'}]; I'm trying to flag in a new attribute called c in full if the object exist on some. Expected result: [{a:'aa1',b:'bb1',c:true},{a:'aa3',b:'bb2'},{a:'aa3',b:'bb3',c:true},{a:'aa2',b:'bb3'}] Some important tips: some always has less elements than full both arrays are sorted equal My current approach is: var getIndexByAB = function(arr, a,b){ var initialIndex = getIndexByAB.initialIndex || 0, len = arr.length; for(initialIndex; initialIndex < len ;initialIndex++ ){ var el = arr[initialIndex]; if( el.b === b && el.a === a ){ getIndexByAB.initialIndex = initialIndex; return initialIndex; } } return -1; } var len = some.length; for(var i = 0; i < len ; i++){ var el=some[i], index = getIndexByAB(full,el.a,el.b); if(index > -1) full[index].c = true; } UPDADE: original solution improved using Juan comment.
Since they are sorted, you can just pass an index to start the search from, that will avoid the O(n^2). You were already doing it, but by storing the index in a global variable. Instead, you should pass it as an argument to getIndexByAB. function getIndexByAB(arr, a,b , initialIndex){ // Was tracking last index by storing it in a global 'this.initialIndex'. // 'this' points to 'window' in global functions. That's bad, it // means this function can't be called on different arrays without // resetting the global // var initialIndex = this.initialIndex || 0, initialIndex = initialIndex || 0; var len = arr.length; for(initialIndex; initialIndex < len ; initialIndex++ ){ var el = arr[initialIndex]; if( el.b === b && el.a === a ){ // Bad globals // this.initialIndex = initialIndex; return initialIndex; } } return -1; } var len = some.length; var lastValidIndex = 0; for(var i = 0; i < len ; i++){ var el = some[i]; // Pass the index here, so it doesn't start from scratch var index = getIndexByAB(full, el.a, el.b, lastValidIndex); if(index > -1) { full[index].c = true; lastValidIndex = index; } } By the way, if you do want a function to cache some values, here's how to do it avoiding globals. (Not that you should use it in this case) var getIndexByAB = (function(){ // This will only be executed once, and is private // to getIndexByAB (all invocations) var lastGoodIndex = 0; return function(arr, a,b, resetIndex){ if (resetIndex) { lastGoodIndex = 0; } var len = arr.length; for(var index = lastGoodIndex; index < len ; index++ ){ var el = arr[index]; if( el.b === b && el.a === a ){ lastGoodIndex = index; return index; } } return -1; }; })(); Alternatively, you could achieve the following by caching it in getIndexByAB.initialIndex but it's not very elegant. The main reason for avoiding this is the fact that getIndexByAB.initialIndex can be modified by anybody else
Since the arrays are both sorted and some is strictly smaller than full, you could save some time by traversing both arrays at the same time with different indexes. As it is, you are traversing full to get the index of the matching element each time, so you have O(N^2) running time, but you only need to continue the search from the last element you matched.
Not as efficient as #Juan's answer (which takes advantage of the sorted nature, among other things), but I thought I'd still present my solution as it incidentally forced me to come up with a solution for cloning and comparing Javacript objects. Utilities // Create a copy of x without reference back to x function clone(x){ return JSON.parse(JSON.stringify(x)); } // Pass any number of arguments of any type. Returns true if they are all identical. function areEqual(){ for(var i = 1, l = arguments.length, x = JSON.stringify(arguments[0]); i < arguments.length; ++i){ if(x !== JSON.stringify(arguments[i])){ return false; } } return true; } Flagging function // Your flagLabel being 'c' function matchAndFlagWith(flagLabel,aFull,aSome){ var aFlagged = clone(aFull); for(var i1 = 0, l1 = aSome.length, oSome; oSome = aSome[i1], i1 < l1; ++i1){ for(var i2 = 0, l2 = aFlagged.length, oFlagged; oFlagged = aFlagged[i2], i2 < l2; ++i2){ if(areEqual(oFlagged,oSome)){ oFlagged[flagLabel] = true; } } } return aFlagged; } Demo http://jsfiddle.net/barney/p2qsG/
Creating A Randomised Array with a Single Pre-defined Choice in JavaScript
I'm creating a program that will ask a question and give 5 choices for answers. One is pre-defined and is correct, the others I want to be random selections from a bank of answers and the entire array is to be shuffled too. I've written something, but it has some inconsistencies. For one, sometimes the pre-defined choice appears twice in the list (it appears to skip over my if check). Another is that sometimes, the editor crashes when I run it. I use for in loops and I'm worried the crash is caused by a never-ending loop. Here's my code: private var numberOfComponents:int; private var maxComponents:int = 5; //numberOfComponents returns the length property of my 'components' answer bank componentsSelection = buildComponentSelectionList(0); //0 is the index of my correct answer function buildComponentSelectionList(correctItemIndex){ var theArray:Array = new Array(); var indicesOfSelection:Array = getIndicesByIncluding(correctItemIndex); Debug.Log(indicesOfSelection); for (var i=0;i<indicesOfSelection.length;i++) theArray.Push(components[indicesOfSelection[i]]); return theArray; } function getIndicesByIncluding(correctItem){ var indicesArray:Array = new Array(); var numberOfChoices = maxComponents-1; for(var i=0;i<numberOfChoices;i++){ var number = Mathf.Round(Random.value*(numberOfComponents-1)); addToRandomNumberSelection(indicesArray, number,correctItem); } indicesArray.Push(correctItem); RandomizeArray(indicesArray); return indicesArray; } function addToRandomNumberSelection(indicesArray:Array,number,correctItem){ if(indicesArray.length == 0){ indicesArray.Push(number); } else { var doesntExist = true; for(var i=0;i<indicesArray.length;i++){ if(indicesArray[i] == correctItem) doesntExist = false; if (indicesArray[i] == number) doesntExist = false; } if(doesntExist) { indicesArray.Push(number); } else { addToRandomNumberSelection(indicesArray, Mathf.Round(Random.value*(numberOfComponents-1)),correctItem); } } } function RandomizeArray(arr : Array) { for (var i = arr.length - 1; i > 0; i--) { var r = Random.Range(0,i); var tmp = arr[i]; arr[i] = arr[r]; arr[r] = tmp; } } The editor is Unity3D, and the code is a version of JavaScript; I think my error is a logic one, rather than a syntactical one. I feel I've been staring at this code for too long now and I'm missing something obvious. Can anybody help me?
You can loop through the options and determine the probability that it should be included, then shuffle the included options: function getRandomOptions(allOptions, correctIndex, count){ var result = [allOptions[correctIndex]]; count--; var left = allOptions.length; for (var i = 0; count > 0; i++) { if (i != correctIndex && Math.floor(Math.random() * left) < count) { result.push(allOptions[i]); count--; } left--; } shuffleArray(result); return result; } function shuffleArray(arr) { for (var i = arr.length - 1; i > 0; i--) { var r = Math.floor(Math.random() * i); var tmp = arr[i]; arr[i] = arr[r]; arr[r] = tmp; } } Demo: http://jsfiddle.net/Guffa/wXsjz/
Collision Script in javascript help needed
Alright so I just tried to cut down on lines of code by changing manually writing out everything into an array.. My problem is that he now teleports and gravity doesnt work... first of all I have a grid of cell objects which basically are a 32x32 grid "640X480". These objects are all passed onto an array like so- var gridcellarray = [750]; gridcellarray[0] = cell0; gridcellarray[1] = cell1; gridcellarray[2] = cell2; and so on for 750 32x32 cells... Now as for the collision script I have this... function collisioncheck(obj) { obj = obj; for(var i = 0; i < gridcellarray.length; i++){ //really long if statement// sorry... if ((gridcellarray[i].solid == true) && ((obj.PosY >= gridcellarray[i].y - obj.maskImg.height) && !(obj.PosY >= gridcellarray[i].y ) && !((obj.PosX > gridcellarray[i].x + solidOriginX + solidImg.width/2-5) || (obj.PosX < gridcellarray[i].x - solidOriginX - solidImg.width/2)))){ if(obj.onground == 0){ obj.PosY = gridcellarray[i].y - obj.maskImg.height; obj.VelY = 0; obj.onground = 1; obj.jump = 0; } } else if (obj.PosY >= canvas.height - obj.maskImg.height){ if(obj.onground == 0){ obj.VelY = 0; obj.onground = 1; obj.jump = 0; obj.PosY = canvas.height - obj.maskImg.height; } } else { obj.VelY += 1; obj.onground = 0; } } } now this code worked just fine before If I had manually copied it 750 times for each cell and the problem is that Now that I have one iteration of it cycling through them as an array it gets confused and teleports me to the bottom and If I try to jump even when not below or on a block it teleports me back to the bottom. //edit I think all the variables should explain their purpose by name but if you have any questions please ask. //edit All Variables apply the object your checking collision for such as collisioncheck(player) here is a link to a running demo of the code... Zack Bloom's code is in it and it works great applied to the unposted horizontal collision scripts but vertically, it wont reset and acknowledge your standing on a block ie ongroud = true; Demo link //edit Ok now that Zack pointed out resetting the y to and x amount it helped alot but he is still not able to jump, as the onground variable doesnt want to reset when the collision is detected... oh and the teleporting is due to my upwards script - //Upwards Collision// if ((cell.solid == true) && ((obj.PosY >= cell.y - 32) && !(obj.PosY > cell.y+32) && !((obj.PosX > cell.x + solidOriginX + solidImg.width/2-5) || (obj.PosX < cell.x - solidOriginX - solidImg.width/2)))){ if (obj.onground == 0){ obj.VelY += 1; obj.onground = 0; obj.PosY = cell.y + obj.maskImg.height-13; } } Any Ideas on how to fix THIS mess above? to stop him from teleporting? It is only meant to check if the top of the collision mask(red rectangle) is touching the block as if trying to jump through it, but it is meant to stop that from happening so you hit your head and fall back down. Thanks in Advance!
The else if / else really don't belong in the loop at all, they don't evaluate the cell being looped over, but will be triggered many times each time collisioncheck is called. function collisioncheck(obj) { for(var i = 0; i < gridcellarray.length; i++){ var cell = gridcellarray[i]; if (cell.solid && ((obj.PosY >= cell.y - obj.maskImg.height) && !(obj.PosY >= cell.y ) && !((obj.PosX > cell.x + solidOriginX + solidImg.width/2-5) || (obj.PosX < cell.x - solidOriginX - solidImg.width/2)))){ if(!obj.onground){ obj.PosY = cell.x - obj.maskImg.height; obj.VelY = 0; obj.onground = 1; obj.jump = 0; break; } } } if (obj.PosY >= canvas.height - obj.maskImg.height){ if(!obj.onground){ obj.VelY = 0; obj.onground = 1; obj.jump = 0; obj.PosY = canvas.height - obj.maskImg.height; } } else { obj.VelY += 1; obj.onground = 0; } } But an even better way of doing it would be to store each gridcell based on where it was, so you just have to look up the gridcells near the ship. // You only have to do this once to build the structure, don't do it every time you // need to check a collision. var gridcells = {}; for (var i=0; i < gridcellarray.length; i++){ var cell = gridcellarray[i]; var row_num = Math.floor(cell.PosX / 32); var col_num = Math.floor(cell.PosY / 32); if (!gridcells[row_num]) gridcells[row_num] = {}; gridcells[row_num][col_num] = cell; } // Then to check a collision: function collisioncheck(obj){ // I'm not sure exactly what your variables mean, so confirm that this will equal // the width of the object: var obj_width = solidImg.width; var obj_height = obj.maskImg.height; var collided = false; var left_col = Math.floor(obj.PosX / 32), right_col = Math.floor((obj.PosX + obj_width) / 32), top_row = Math.floor(obj.PosY / 32), bottom_row = Math.floor((obj.PosY + obj_height) / 32); for (var row=top_row; row <= bottom_row; row++){ for (var col=left_col; col <= right_col; col++){ var cell = gridcells[row][col]; if (cell.solid){ collided = true; if (row == top_row){ // We collided into something above us obj.VelY = 0; obj.PosY = cell.PosY + 32; } else if (row == bottom_row && !obj.onground){ // We collided into the ground obj.PosY = cell.x - obj_height; obj.VelY = 0; obj.onground = 1; obj.jump = 0; } if (col == left_col){ // We collided left obj.VelX = 0; obj.PosX = cell.PosX + 32; } else if (col == right_col){ // We collided right obj.VelX = 0; obj.PosX = cell.PosX - obj_width; } } } } if (obj.PosY >= canvas.height - obj_height){ if (!obj.onground){ obj.VelY = 0; obj.onground = 1; obj.jump = 0; obj.PosY = canvas.height - obj_height; } } if (!collided){ obj.VelY += 1; obj.onground = 0; } } Rather than looping through 720 cells each frame, we are only looking at the cells we know we are overlapping. This is much more efficient and easier to read than all the position calculations.
Some comments on your code: var gridcellarray = [750]; That creates an array with a single member that has a value of 750. I think you are assuming that it is equivalent to: var gridcellarray = new Array(750); which creates an array with a length of 750. There is no need to set the size of the array, just initialise it as an empty array and assign values. var gridcellarray = []; gridcellarray[0] = cell0; This replaces the value of the first member with whatever the value of cell0 is. function collisioncheck(obj) { obj = obj; There is no point in the second line, it just assigns the value of obj to obj (so it's redundant). for(var i = 0; i < gridcellarray.length; i++){ It is much more efficient in most browsers to save the value of gridcellarray.length, otherwise it must be looked up every time (the compiler may not be able to work whether it can cache it), so: for (var i = 0, iLen = gridcellarray.length; i < iLen; i++) { It is more efficient to store a reference to gridcellarray[i] rather than look it up every time. Also, you can use a short name since it's only used within the loop so its purpose is easily found: var c = gridcellarray[i]; so not only will the code run faster, but the if statement has fewer characters (and you might prefer to format it differently): if ((c.solid == true) && ((obj.PosY >= c.y - obj.maskImg.height) && !(obj.PosY >= c.y ) && !((obj.PosX > c.x + solidOriginX + solidImg.width/2-5) || (obj.PosX < c.x - solidOriginX - solidImg.width/2)))) { Wow, that really is some if statement. Can you break it down into simpler, logical steps? Ah, Zack has posted an answer too so I'll leave it here.