javascript dynamically create multidimensional array from string - javascript

<span id="local->ethernet->port3->rx_flow">q4234</span>
<span id="local->ethernet->port3->rx">q345</span>
<span id="local->ethernet->port1->rx_flow">128</span>
<span id="remote->id">128</span>
and I need to make multidimensional array from them by ID
example from element <span id="local->ethernet->port3->rx_flow">q4234</span>
array I need is array["local"]["ethernet"]["port3"]["rx_flow"]="q4234"
function I created is:
function make_cfg(){
var result=new Array();
var x=document.getElementById(*);
var len=x.length;
var arr;
for (var i=0; i<=len; i++;){
if(x[i].id){
if(x[i].id.indexOf("->") != -1) {
arr=x[i].id.split("->");
result=make_obj(result,arr);
}
}
}
return result;
}
And I have no idea how to make function make_obj()

I won't write the whole thing for you, I just help with the hard part a bit.
This snippet will take the two strings (basically id and innerHTML, here s and s2) and construct a nested object (there are no associative arrays in Javascript) out of it.
var s='local->ethernet->port3->rx_flow',
s2='q4234',
a=s.split('->'),
obj=constructObject(a, s2);
function constructObject(a, final) {
var val=a.shift();
var obj={};
if (a.length>0) {
obj[val]=constructObject(a, final);
} else {
obj[val]=final;
}
return obj;
}
It uses recursion to achieve its goal. If you have any questions about the code, please ask.
Here you can try it out.
What is left to do?
I guess you want to collect these things from the spans into ONE object, my example will create one object for every s / s2. If you have any further questions, I am happy to help.

this almost worked (not so elegant as a recursive function)
http://jsfiddle.net/mplungjan/3zhwv/
Missing the 128 from the remote/id but the rest works. I would like to figure out what to do to get the 128 from the node that is shorter than 4
I agree it is not flexible like a recursive function, but I wanted to see if I could make a "brute force" first and then par it down to something more clever.
<span id="local->ethernet->port3->rx_flow">q4234</span>
<span id="local->ethernet->port3->rx">q345</span>
<span id="local->ethernet->port1->rx_flow">128</span>
<span id="remote->id">128</span>
<hr/>
<pre>
myObject = {
"local":{
"ethernet":{
"port3": {
"rx_flow":"q4234",
"rx":"q345"
}
"port1": {
"rx_flow":"128"
}
}
},
"remote":{
"id":"128"
}
}
</pre>
<script>
var spans = document.getElementsByTagName("span");
var myObject = {};
for (var i=0;i < spans.length;i++) {
var id = spans[i].id;
var parts = id.split('->');
var val = spans[i].innerHTML
if (parts[0]) { // Local or remote
if (myObject[parts[0]] == null) myObject[parts[0]]={};
if (parts[1]) { // ethernet or id
if (myObject[parts[0]][parts[1]] == null) myObject[parts[0]][parts[1]]=(parts.length==1)?val:{};
if (parts[2]) { // port3 or port1
if (myObject[parts[0]][parts[1]][parts[2]] == null) myObject[parts[0]][parts[1]][parts[2]]=(parts.length==2)?val:{};
if (parts[3]) { // rx_flow or rx
myObject[parts[0]][parts[1]][parts[2]][parts[3]]=val;
}
}
}
}
}
for (var o in myObject) { // local or remote
document.write(o+'/');
for (var p in myObject[o]) { // ethernet or id
document.write(p+'/');
for (var q in myObject[o][p]) { // ports
document.write(q+':/');
for (var r in myObject[o][p][q]) { // rx_flow or rx
document.write(r+' - '+myObject[o][p][q][r]+'<br/>');
}
}
}
}
</script>

Related

Split a String Array and Store Values for each Iteration

So the variable classList stores all classes for the body. I then created a variable classListLength that has the length of classList so I can iterate through each index and then split each class. I do not know how to store the splits for each index as it loops through classList. Help me please.
var classList = jQuery('body').attr('class').split(' ');
var classListLength = classList.length;
var keyWords = function(array) {
for (var i = 0; i < classListLength; i++ ) {
classList[i].split('-');
}
}
if i do the following in the console
var keyWords = function(array) {
for (var i = 0; i < classListLength; i++ ) {
console.log(classList[i].split('-'));
}
}
I can see exactly what I want but I want to be able to store that and check it later on with a conditional.
var splitClassList = classList.map(function (class) {
return class.split('-');
});
So I solved what I needed. The below code allows me to iterate through my split classList. I then check the the specific class I want within the classList using .includes method and execute what I need done. If you guys know how to make this a bit more modular please chime in.
var brandClass
// Iterate though split classes
jQuery.each( classList, function(i) {
if ( classList[i].includes('product-wildridge') ) {
brandClass = classList[i];
}
});
// check specific class for certin keywords
var customTab = function(brandClass) {
if (brandClass.includes('wildridge') && brandClass.includes('deep') ) {
return true;
} else {
jQuery('#tab-fabric').css('display', 'none');
}
}
customTab(brandClass);

Can I use a loop to optimize my code?

I'm trying to optimize my code to be more efficient and easier to read. I have some combined if-statements, which I think could be better, if they are transformed to for-loops, I'm just not sure how to do this?
This is my code:
if (starportSelected){
if(game.currentLevel.requirements.vehicles.indexOf('transport')>-1 && cashBalance>=vehicles.list["transport"].cost){
$("#transportbutton").removeAttr("disabled");
}
if(game.currentLevel.requirements.vehicles.indexOf('scout-tank')>-1 && cashBalance>=vehicles.list["scout-tank"].cost){
$("#scouttankbutton").removeAttr("disabled");
}
if(game.currentLevel.requirements.vehicles.indexOf('heavy-tank')>-1 &&cashBalance>=vehicles.list["heavy-tank"].cost){
$("#heavytankbutton").removeAttr("disabled");
}
if(game.currentLevel.requirements.vehicles.indexOf('harvester')>-1 && cashBalance>=vehicles.list["harvester"].cost){
$("#harvesterbutton").removeAttr("disabled");
}
if(game.currentLevel.requirements.aircraft.indexOf('chopper')>-1 && cashBalance>=aircraft.list["chopper"].cost){
$("#chopperbutton").removeAttr("disabled");
}
if(game.currentLevel.requirements.aircraft.indexOf('wraith')>-1 && cashBalance>=aircraft.list["wraith"].cost){
$("#wraithbutton").removeAttr("disabled");
}
}
I think first step would be to create two arrays, one for vehicles and one for aircrafts like this:
var vehicles = ['transport', 'scout.tank', 'heavy-tank', 'harvester'];
var aircraft = ['chopper', 'wraith'];
But how to take the rest of the code and change it to for-loop seems like a hard case for me.
All help and explanation would be highly appreciated!
Seems that you have "vehicles" and "aircraft" types, with multiple values for each.
As such, I'd create an object of types to arrays of values.
Because you're also using a variable named vehicles and aircraft, you'll want to reference those in a separate object so that you can look them up with a string.
var lists = {
vehicles: vehicles,
aircraft: aircraft
}
var obj = {
vehicles: ["transport", "scout-tank", "heavy-tank", "harvester"],
aircraft: ["chopper", "wraith"]
};
Then use an outer and inner loop.
// v---("vehicles" or "aircraft")
for (var type in obj) { // v---("transport", "scout-tank", "chopper", etc...)
obj[type].forEach(function(val) {
if(game.currentLevel.requirements[type].indexOf(val)>-1 &&
cashBalance >= lists[type].list[val].cost) {
$("#" + val.replace("-", "") + "button").removeAttr("disabled");
}
});
}
Notice also that I had to replace the hyphen in the ID selector, since it wasn't used as part of the ID.
The two objects at the top could be combined into a single one if you wish:
var obj = {
vehicles: {
list: vehicles,
values: ["transport", "scout-tank", "heavy-tank", "harvester"]
},
aircraft: {
list: aircraft,
values: ["chopper", "wraith"]
}
};
Then adjust the loop references accordingly.
I've also cached the objects for performance, as Jared noted above.
for (var type in obj) {
var list = obj[type].list;
var requirements = game.currentLevel.requirements[type];
obj[type].values.forEach(function(val) {
if(requirements.indexOf(val)>-1 && cashBalance >= list[val].cost) {
$("#" + val.replace("-", "") + "button").removeAttr("disabled");
}
});
}
To make it even more efficient than the original, we'll drop some of the jQuery calls.
for (var type in obj) {
var list = obj[type].list;
var requirements = game.currentLevel.requirements[type];
for (var i = 0, vals = obj[type].values; i < vals.length; i++) {
var val = vals[i];
if(requirements.indexOf(val) > -1 && cashBalance >= list[val].cost) {
document.getElementById(val.replace("-", "") + "button").disabled = false;
}
}
}

Alternatives to eval() for multiple nested objects

I'm trying to create a generic i18n solution for a HTML app I'm working in. I'm looking for alternatives to use eval() to call deeply nested Javascript objects:
Suppose the following HTML example:
<div id="page1">
<h1 data-i18n="html.pageOne.pageTitle"></h1>
</div>
and it's companion Javascript (using jQuery):
var i18n;
i18n = {
html: {
pageOne: {
pageTitle: 'Lorem Ipsum!'
}
}
};
$(document).ready(function () {
$('[data-18n]').each(function () {
var q;
q = eval('i18n.' + $(this).attr('data-i18n'));
if (q) {
$(this).text(q);
}
});
});
Any advices on how to access the "pageTitle" property inside the i18n object without using eval()? I need to keep the object's structure, so changing its layout to a "flat" solution is not feasible.
Thanks!!!
You can use bracket syntax, as others have hinted at. But, you'll need to split and iterate at .:
function lookup(obj, path) {
var keys = path.split('.'),
result = obj;
for (var i = 0, l = keys.length; i < l; i++) {
result = result[keys[i]];
// exit early if `null` or `undefined`
if (result == null)
return result;
}
return result;
}
Then:
q = lookup(i18n, $(this).attr('data-i18n'));
if (q) {
$(this).text(q);
}
The dot syntax (object.field) is really just syntactic sugar for object['field']. If you find yourself writing eval('object.'+field), you should simply write object['field'] instead. In your example above, you probably want: i18n[$(this).attr('data-i18n')].
Since you're encoding your attribute in a way that has dots in it, try splitting it by the dots, and iterating over the fields. For example (this can probably be improved):
var fields = $(this).attr('i18n').split('.');
fieldCount = fields.length;
fieldIdx = 0;
var cur = i18n;
while(cur != undefined && fieldIdx > fieldCount) {
cur = cur[fields[fieldIdx++]];
}
You'll want to do additional checking to make sure all of the fields were handled, nulls weren't encountered, etc.
You can split the string on the periods and traverse the object:
var q = i18n;
$.each($(this).attr('data-i18n').split('.'), function(index, key){
if (q) q = q[key];
});
Demo: http://jsfiddle.net/GsVsr/

javascript not removing undefined objects from array

I've got an in page text search using JS, which is here:
$.fn.eoTextSearch = function(pat) {
var out = []
var textNodes = function(n) {
if (!window['Node']) {
window.Node = new Object();
Node.ELEMENT_NODE = 1;
Node.ATTRIBUTE_NODE = 2;
Node.TEXT_NODE = 3;
Node.CDATA_SECTION_NODE = 4;
Node.ENTITY_REFERENCE_NODE = 5;
Node.ENTITY_NODE = 6;
Node.PROCESSING_INSTRUCTION_NODE = 7;
Node.COMMENT_NODE = 8;
Node.DOCUMENT_NODE = 9;
Node.DOCUMENT_TYPE_NODE = 10;
Node.DOCUMENT_FRAGMENT_NODE = 11;
Node.NOTATION_NODE = 12;
}
if (n.nodeType == Node.TEXT_NODE) {
var t = typeof pat == 'string' ?
n.nodeValue.indexOf(pat) != -1 :
pat.test(n.nodeValue);
if (t) {
out.push(n.parentNode)
}
}
else {
$.each(n.childNodes, function(a, b) {
textNodes(b)
})
}
}
this.each(function() {
textNodes(this)
})
return out
};
And I've got the ability to hide columns and rows in a table. When I submit a search and get the highlighted results, there would be in this case, the array length of the text nodes found would be 6, but there would only be 3 highlighted on the page. When you output the array to the console you get this:
So you get the 3 tags which I was expecting, but you see that the array is actually consisting of a [span,undefined,span,undefined,undefined,span]. Thus giving me the length of 6.
<span>
<span>
<span>
[span, undefined, span, undefined, undefined, span]
I don't know why it's not stripping out all of the undefined text nodes when I do the check for them. Here's what I've got for the function.
performTextSearch = function(currentObj){
if($.trim(currentObj.val()).length > 0){
var n = $("body").eoTextSearch($.trim(currentObj.val())),
recordTitle = "matches",
arrayRecheck = new Array(),
genericElemArray = new Array()
if(n.length == 1){
recordTitle = "match"
}
//check to see if we need to do a recount on the array length.
//if it's more than 0, then they're doing a compare and we need to strip out all of the text nodes that don't have a visible parent.
if($(".rows:checked").length > 0){
$.each(n,function(i,currElem){
if($(currElem).length != 0 && typeof currElem != 'undefined'){
if($(currElem).closest("tr").is(":visible") || $(currElem).is(":visible")){
//remove the element from the array
console.log(currElem)
arrayRecheck[i] = currElem
}
}
})
}
if(arrayRecheck.length > 0){
genericElemArray.push(arrayRecheck)
console.log(arrayRecheck)
}
else{
genericElemArray.push(n)
}
genericElemArray = genericElemArray[0]
$("#recordCount").text(genericElemArray.length + " " +recordTitle)
$(".searchResults").show()
for(var i = 0; i < genericElemArray.length; ++i){
void($(genericElemArray[i]).addClass("yellowBkgd").addClass("highLighted"))
}
}
else{
$(".highLighted").css("background","none")
}
}
If you look at the code below "//check to see if we need to do a recount on the array length. ", you'll see where I'm stripping out the text nodes based off of the display and whether or not the object is defined. I'm checking the length instead of undefined because the typeof == undefined wasn't working at all for some reason. Apparently, things are still slipping by though.
Any idea why I'm still getting undefined objects in the array?
My apologies for such a big post!
Thanks in advance
I've modified your eoTextSearch() function to remove dependencies on global variables in exchange for closures:
$.fn.extend({
// helper function
// recurses into a DOM object and calls a custom function for every descendant
eachDescendant: function (callback) {
for (var i=0, j=this.length; i<j; i++) {
callback.call(this[i]);
$.fn.eachDescendant.call(this[i].childNodes, callback);
}
return this;
},
// your text search function, revised
eoTextSearch: function () {
var text = document.createTextNode("test").textContent
? "textContent" : "innerText";
// the "matches" function uses an out param instead of a return value
var matches = function (pat, outArray) {
var isRe = typeof pat.test == "function";
return function() {
if (this.nodeType != 3) return; // ...text nodes only
if (isRe && pat.test(this[text]) || this[text].indexOf(pat) > -1) {
outArray.push(this.parentNode);
}
}
};
// this is the function that will *actually* become eoTextSearch()
return function (stringOrPattern) {
var result = $(); // start with an empty jQuery object
this.eachDescendant( matches(stringOrPattern, result) );
return result;
}
}() // <- instant calling is important here
});
And then you can do something like this:
$("body").eoTextSearch("foo").filter(function () {
return $(this).closest("tr").is(":visible");
});
To remove unwanted elements from the search result. No "recounting the array length" necessary. Or you use each() directly and decide within what to do.
I cannot entirely get my head around your code, but the most likely issue is that you are removing items from the array, but not shrinking the array afterwards. Simply removing items will return you "undefined", and will not collapse the array.
I would suggest that you do one of the following:
Copy the array to a new array, but only copying those items that are not undefined
Only use those array items that are not undefined.
I hope this is something of a help.
Found the answer in another post.
Remove empty elements from an array in Javascript
Ended up using the answer's second option and it worked alright.

Finding value within javascript array

I'm trying to set up an IF statement if a value is contained within an array.
I've found some code which claimed to work but it doesn't seem to be.
var myAsi = ['01','02','24OR01','30De01','9thC01','A.Hu01','A01','AACAMSTE','ABBo01','ABBo02','ABC-01','ACCE01','Acce02','AceR01','h+dm01','Merr02','Ofak01','Wage01','Youn01'];
Array.prototype.find = function(searchStr) {
var returnArray = false;
for (i=0; i<this.length; i++) {
if (typeof(searchStr) == 'function') {
if (searchStr.test(this[i])) {
if (!returnArray) { returnArray = [] }
returnArray.push(i);
}
} else {
if (this[i]===searchStr) {
if (!returnArray) { returnArray = [] }
returnArray.push(i);
}
}
}
return returnArray;
}
var resultHtml = '';
resultHtml+='<table style ="width: 400px">';
resultHtml+='<tr colspan="2">';
resultHtml+='<td colspan="2">';
resultHtml+='<b><font color = "Red">(Client Code)</font><br><font color = "green">(Company Name)</font></b>';
resultHtml+='</td>';
resultHtml+='</tr>';
$.each(data, function(i,item){
resultHtml+='<div class="result">';
resultHtml+='<tr>';
if (notFound=myAsi.find("'"+item.code+"'") == false) {
resultHtml+='<td>';
}
else {
resultHtml+='<td bgcolor=#D8D8D8>';
}
resultHtml+='<font color = "red">'+item.code+'</font><br>';
resultHtml+='<font color = "green">'+item.content+'</font></td>';
resultHtml+='<td style ="width: 80px">Remove - ';
resultHtml+='Add';
resultHtml+='</td>';
resultHtml+='</tr>';
resultHtml+='</div>';
});
resultHtml+='</table>';
The item.code cycles through and I need an IF statement to tell me if it appears within the array.
Any help would be great.
If you only want to find if an item is in an array you could use a simpler function than that. For eg. the jQuery implementation:
// returns index of the element or -1 if element not present
function( elem, array ) {
if ( array.indexOf ) {
return array.indexOf( elem );
}
for ( var i = 0, length = array.length; i < length; i++ ) {
if ( array[ i ] === elem ) {
return i;
}
}
return -1;
},
This uses the native browser implementation of indexOf if available (all browsers except IE I think), otherwise a manual loop.
Try removing the apostrophes from your find() call. eg
notFound=myAsi.find(item.code)
Though actually, for your purposes see this example which uses this function....
Array.prototype.find = function(searchStr) {
for (var i=0; i<this.length; i++) {
if (this[i]==searchStr) return true;
};
return false;
};
And as an aside - Be very careful about using var before using a variable - otherwise you create a global variable (which you probably don't want). ie the line in your original function....
for (i=0; i<this.length; i++)
i is now global...
Array.prototype.contains = function(value, matcher) {
if (!matcher || typeof matcher !== 'function') {
matcher = function(item) {
return item == value;
}
}
for (var i = 0, len = this.length; i < len; i++) {
if (matcher(this[i])) {
return true;
}
}
return false;
};
This returns true for elements in the array that statisfy the conditions defined in matcher. Implement like this:
var arr = ['abc', 'def', 'ghi']; // the array
var valueToFind= 'xyz'; // a value to find in the array
// a function that compares an array item to match
var matcher = function(item) {
return item === matchThis;
};
// is the value found?
if (arr.contains(valueToFind, matcher)) {
// item found
} else {
// item not found
}
UPDATES:
Changed the contains method to take a value and an optional matcher function. If no matcher is included, it will do a simple equality check.
Test this on jsFiddle.net: http://jsfiddle.net/silkster/wgkru/3/
You could just use the builtin function
['a','b','c'].indexOf('d') == -1
This behavior was mandated in the javascript specification from over 6 years ago. Though I gave up on Internet Explorer for these reasons at around IE8, because of this incredibly poor support for standards. If you care about supporting very old browsers, you can use http://soledadpenades.com/2007/05/17/arrayindexof-in-internet-explorer/ to tack on your own custom Array.indexOf
I don't recall IE9 supporting [].indexOf, but Microsoft claims it does: http://msdn.microsoft.com/en-us/library/ff679977(v=VS.94).aspx
The standard way to determine the index of the first occurence of a given value in an array is the indexOf method of Array objects.
This code checks if it this method is supported, and implements it if not, so that it is available on any Array object:
if(Array.prototype.indexOf==null)
Array.prototype.indexOf = function(x){
for(var i=0, n=this.length; i<n; i++)if(this[i]===x)return i;
return -1;
};
Now myArray.indexOf(myValue) returns the first index of myValue in myArray, or -1 if not found.

Categories

Resources