Can I use a loop to optimize my code? - javascript

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;
}
}
}

Related

Weird issue with Vue / Javascript variables

I honestly don't even know how to search for this question (what search param to write) but either way its bit weird issue and I am desperate for help.
So I am trying to do something simple, event sends "form-change" and when it does, we set new value in "this.data" object. Fairly simple. I don't expect this.data to be reactive I just want to update it.
// Find our data object which we want to update/change
if (form.field.includes('.')) {
let find = form.field.split('.'), level = this.data;
for (let index = 0; index < find.length; index++) {
if (level[find[index]] !== undefined) {
level = level[find[index]];
} else {
level = undefined;
}
}
if (level !== undefined)
level = setFieldData();
}
This is fairly simple, we have name of field "inspect.book" and when update comes (Event) we just use dots to split into multi tree and update "this.data.inspect.book" to new value. But it does not work. Value does not change.
But the value from actual this.data.inspect.book comes out just fine using:
console.log(level);
However, if I do this:
this.data[ form.field.split( '.' )[ 0 ] ][ form.field.split( '.' )[ 1 ] ] = setFieldData();
It works fine. So "reference" to variable does not work... how is this possible? Looks like a bug in javascript or is it something with vue/reactivity?
Does anyone have better idea how to get this to work?
So you are trying to update form data using to notation ?
i would do something like that :
_update(fieldName, value) {
// We split segments using dot notation (.)
let segments = fieldName.split(".");
// We get the last one
let lastSegment = segments.pop();
// We start with this.data as root
let current = this.data
if(current) {
// We update current by going down each segment
segments.forEach((segment) => {
current = current[segment];
});
// We update the last segment once we are down the correct depth
current[lastSegment] = val;
}
},
if i take your example :
if (form.field.includes('.')) {
let find = form.field.split('.'), level = this.data;
for (let index = 0; index < find.length - 1; index++) {
if (level[find[index]] !== undefined) {
level = level[find[index]];
} else {
level = undefined;
}
}
if (level !== undefined)
level[find.pop()] = setFieldData();
}
i replaced find.length by find.length - 1
and replaced level = setFieldData() by level[find.pop()] = setFieldData()
you should update the property of the object, without actually overriding the value,
because if you override the value, the original value will not get updated.

JSON.stringify() causing an infinite loop (Edit: NO. Bad logic is.)

My product object looks somewhat like this:
{name: 'abc', description: 'def', price: 100, quantity: 1, comment: '', customizations: []}
The customizations key is an array that has other such product objects in it. You may ignore it for this question. As you might have noticed, the comment and customizations keys are the keys that make the (theoretically) same product (practically) different when adding to cart.
I want to make a function to add such products to an array called cart_items[]. If the (practically) same product is chosen, I only want to increment the quantity inside the cart_items[i], else add a new object.
This is my function:
$scope.add_to_cart = function(product) {
// if the cart is empty, skip the brouhaha
if ($scope.cart_items.length === 0) {
$scope.cart_items.push(angular.copy(product));
} else {
for (var i = 0; i < $scope.cart_items.length; i++) {
// copy the original quantity and set it to 1 for comparison
var qty = $scope.cart_items[i].quantity;
$scope.cart_items[i].quantity = 1;
if (JSON.stringify(product) === JSON.stringify($scope.cart_items[i])) {
$scope.cart_items[i].quantity = qty + 1;
} else {
$scope.cart_items[i].quantity = qty;
$scope.cart_items.push(angular.copy(product));
}
}
}
};
The problem: First product adds successfully. Adding another causes an infinite loop. I replaced if(JSON...) with if(1 === 1) and the infinite loop didn't occur. I don't know where am I going wrong. Any help?
The problem you have is that you're increasing the size of the array in the loop and the stop condition is i being $scope.cart_items.length.
If your goal is to add the object if it's not yet present, what you want is probably this :
boolean found = false;
for (var i = 0; i < $scope.cart_items.length; i++) {
var qty = $scope.cart_items[i].quantity;
$scope.cart_items[i].quantity = 1;
if (JSON.stringify(product) === JSON.stringify($scope.cart_items[i])) {
$scope.cart_items[i].quantity = qty + 1;
found = true;
break;
}
}
if (!found) {
var item = angular.copy(product);
item.quantity = 1;
$scope.cart_items.push();
}
Note that two identical objects (i.e. objects with same property values) should not give the same JSON string because the order of property iteration isn't specified. It usually works (especially if the cloning is done the obvious way) but there's no guarantee. You really should compare based on the properties, not using JSON stringification.
You probably enter in a recursive loop, because of objects contained in customizations...
A [ customizations = B ]
B [ customizations = C ]
C [ customizations = A ]
----------------------------
Infinite loop

Filter a store with array of values from one property with ExtJS

I'm trying to apply a constraint on combobox. It's half-working at the moment.
On the combobox, I have this listener:
[...]
listeners: {
'focus': function (combo, value) {
var orgComboVal = Ext.getCmp('Org1')
var getOrgValue = orgComboVal.getValue();
if (typeof getOrgValue !== undefined) {
reseaulist.clearFilter(true);
for (var q = 0, l = getOrgValue.length; q < l; q++) {
reseaulist.filter([
{property:'code_organisme', value: getOrgValue[q]}
]);
}
}
}
}
Ext.getCmp('Org1') defines another combobox.
When orgComboVal.getValue() is a single value, the filter is well applying.
but when it's an array of value, eg ["5", "9"], it's not working and the combobox supposed to be filtered shows no value (so I guess a filter is still applied but in an incorrect way).
I guess it's because the reseaulist.filter is called multiple time.
How can I achieve this ?
I saw the filterBy method but I don't know how to make it work.
Also, this post is interesting : How to filter a store with multiple values at once? but same, can't make it work since
getOrgValue.split(',')
is showing an error
(Object Array has no method split)
Any tips ? I'm using ExtJS 4.2.
EDIT
Thanks to #rixo, I've made it.
Also, I had to change some of the code he provided me, because the value of the Org1 combobox was always an array, even if empty, so the store filter was never cleared.
Here it is :
'focus': function (combo, value) {
var orgComboVal = Ext.getCmp('Org1')
var values = orgComboVal.getValue();
console.log(values)
if (values != null) {
reseaulist.clearFilter(false);
if (Ext.isArray(values)) {
if (0 < values.length) {
reseaulist.filterBy(function(record, id) {
return Ext.Array.contains(values, record.get('code_organisme'));
});
} else {
reseaulist.clearFilter(true);
}
}
}
}
Each filter is applied one after the other on the previously filtered data set, so your code implements a logical AND. That's why all values are filtered out...
Here's an example using filterBy to accept any value that is in your array:
function (combo, value) {
var orgComboVal = Ext.getCmp('Org1')
var values = orgComboVal.getValue();
if (values != null) {
store.clearFilter(false);
if (Ext.isArray(values)) {
store.filterBy(function(record, id) {
return Ext.Array.contains(values, record.get('code_organisme'));
});
} else {
record.get('code_organisme') === values;
}
} else {
store.clearFilter(true);
}
}
Or you could also use a regex with the filter method:
function (combo, value) {
var orgComboVal = Ext.getCmp('Org1')
var values = orgComboVal.getValue();
if (values != null) {
var filterValue = Ext.isArray(values)
? new RegExp('^(?:' + Ext.Array.map(values, function(value){return Ext.escapeRe(value)}).join('|') + ')$')
: values;
store.clearFilter(false);
store.filter('code_organisme', filterValue);
} else {
store.clearFilter(true);
}
}
Concerning your error, arrays indeed don't have a split method. Strings can be split into an array. Arrays, on their side, can be joined into a string...
Try This....
var getOrgValue = "5,9,4"; // array of value
reseaulist.filterBy(function(rec, id) {
return getOrgValue.indexOf(rec.get('code_organisme')) !== -1;
});

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.

javascript dynamically create multidimensional array from string

<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>

Categories

Resources