Trying to check if object exists in Knockout Observable Array - javascript

I'm trying to check if an object has the same observable values of other objects with the same observable properties inside an observable array.
I created a foreach loop which evaluates if any of the observables match. The problem I'm having is that condition always evaluates to true, even though these values are different. I'm using typescript and knockout.
Here's the code :
export function addPDFToPackage(heat: MTRHeat): void {
var koHeat: MTRHeatWithInclude = ko.mapping.fromJS(heat);
koHeat.Include = ko.observable(true);
var arrayOfHeats = model.mtrPackage.Heats();
var addToHeats = () => model.mtrPackage.Heats.push(koHeat);
var duplicate = false;
arrayOfHeats.forEach(function (koHeat, i) {
if (arrayOfHeats[i].MTRID() == koHeat.MTRID() && arrayOfHeats[i].HeatID() == koHeat.HeatID() && arrayOfHeats[i].PartID() == koHeat.PartID()) {
duplicate = true;
}
else
duplicate = false;
})
if (!!model.mtrPackage.PackageID()) {
if (duplicate) {
var c = confirm("Warning: Duplicate MTR located on current package.Proceed ?")
if (c) {
ServiceMethods.addHeatToPackage(model.mtrPackage.PackageID(), heat.HeatID).done(addToHeats);
}
if (!c) {
return;
}
}
}
}

First problem: Your loop compares each object to itself because you re-use the variable name koHeat. I believe you really wanted to refer to the "outer" koHeat.
Second problem: You overwrite the duplicate variable in every loop iteration. This is probably not what you intend. Instead you want to stop the loop as soon as a duplicate is found.
How about something along those lines?
export function addPDFToPackage(heat: MTRHeat): void {
var koHeat: MTRHeatWithInclude = ko.mapping.fromJS(heat);
var packageId = model.mtrPackage.PackageID();
koHeat.Include = ko.observable(true);
function equals(a: MTRHeatWithInclude, b: MTRHeatWithInclude): boolean {
return a.MTRID() == b.MTRID() && a.HeatID() == b.HeatID() && a.PartID() == b.PartID();
}
if ( !!packageId && (
!model.mtrPackage.Heats().some(item => equals(item, koHeat)) ||
confirm("Warning: Duplicate MTR located on current package.Proceed ?")
)
) {
ServiceMethods.addHeatToPackage(packageId, heat.HeatID).done(() => {
model.mtrPackage.Heats.push(koHeat);
});
}
}
The equals() function should ideally be a method of the MTRHeatWithInclude class.

I think you're getting a clash between koHeat defined here:
var koHeat: MTRHeatWithInclude = ko.mapping.fromJS(heat);
koHeat.Include = ko.observable(true);
And the variable defined within the forEach call. It's always returning true as (within the scope of the forEach) arrayOfHeats[i] === koHeat.
Try this:
export function addPDFToPackage(heat: MTRHeat): void {
var koHeat: MTRHeatWithInclude = ko.mapping.fromJS(heat);
koHeat.Include = ko.observable(true);
var arrayOfHeats = model.mtrPackage.Heats();
var addToHeats = () => model.mtrPackage.Heats.push(koHeat);
var duplicate = false;
arrayOfHeats.forEach(function (koHeat2, i) {
if (koHeat2.MTRID() == koHeat.MTRID() &&
koHeat2.HeatID() == koHeat.HeatID() &&
koHeat2.PartID() == koHeat.PartID()) {
duplicate = true;
}
})
if (!!model.mtrPackage.PackageID()) {
if (duplicate) {
var c = confirm("Warning: Duplicate MTR located on current package.Proceed ?")
if (c) {
ServiceMethods.addHeatToPackage(model.mtrPackage.PackageID(), heat.HeatID).done(addToHeats);
} else {
return;
}
}
}
}

Related

Target Variable from Variable Value

I am trying to target a variable from a variable value that has been passed in from function arguments. I don't know how to do this. P.S. the variables at the top are also used by other functions
let cardvalue0 = false;
let cardvalue1 = false;
function myfunction (card) {
if (card === false) {
card = true;
//ether cardValue 0 or 1 should now be true
return card;
}
}
// exturnal html
<button onclick="myfunction("cardValue0")"></button>
<button onclick="myfunction("cardValue1")"></button>
new try by me
//define cards
let card0State = false;
let card1State = false;
//toggle card status (read or not)
function cardToggle(card) {
console.log(card);
card = !card
console.log(card);
return card;
}
// external html
<button onclick="myfunction(cardValue0)"></button>
<button onclick="myfunction(cardValue1)"></button>
target a variable from a variable value? If I understood you right you want to access a variable cardvalue0 inside the function myfunction.
You can access it using, eval(card) but definitely not advised to do
function myfunction (card) {
if (eval(card) === false) { //this will work
card = true; //however you cannot change the original value
return card;
}
}
You can get this by using objects
let cardDict = { "cardvalue0" : false,
"cardvalue1" : false}
function myfunction (card) {
if (cardDict[card] === false) {
cardDict[card] = true;
return cardDict[card];
}
}
Would it be possible to store card0State and card1State in an object instead? Then you could reference them as keys to the object instead.
const cardvalues = {};
cardvalues.cardvalue0 = false;
cardvalues.cardvalue1 = false;
function myfunction (cardNumber) {
if (cardvalues[cardNumber] === false) {
cardvalues[cardNumber] = true;
//ether cardValue 0 or 1 should now be true
return cardvalues[cardNumber];
}
}
// exturnal html
<button onclick="myfunction('cardvalue0')"></button>
<button onclick="myfunction('cardvalue1')"></button>
Then you aren't declaring a new variable in the function scope and you are directly altering the key values on the object. This is usually better than a variable variable pattern.
"Variable" variables in Javascript?
the simpler would be moving the original variable (cardvalue0 and cardvalue1) in a Map or an object, so you could have :
cards = {
cardvalue0 : false,
cardvalue1 : false
}
function foo(cardName) {
cards[cardName] = !cards[cardName];
return cards[cardName]
}
}
if you can not move cardvalue0 and cardvalue1, you could simply hardcode them, but it will become ugly really fast.
function foo(cardName) {
if(cardName === "cardvalue0") {
cardValue0 = !cardValue0;
return cardValue0;
} else if(cardName === "cardvalue1")
cardValue1 = !cardValue1;
return !cardValue1;
}
eval would be the only way to get a variable from a string representation, but is not a good idea.

Define this function outside of a loop

I have the following code :
for (var entry in metadata) {
if (metadata.hasOwnProperty(entry)) {
var varName = metadata[entry].variableName;
if (metadata[entry].multipleValues === "false") {
if (angular.isDefined(vm[varName]) && (vm[varName] !== null) && vm[varName].id !== null) {
filters.push(factory.buildEntry(metadata[entry].variableName, vm[varName].id, null, factory.filterOperators.textContains));
}
} else {
if (angular.isDefined(vm[varName]) && (angular.isArray(vm[varName])) && (vm[varName].length > 0)) {
filters.push(factory.buildEntry(metadata[entry].variableName, null, vm[varName].map(function (item) {
return item.id;
}), factory.filterOperators.textContains));
}
}
}
}
But SonarQube keeps telling me to Define this function outside of a loop., and the only function I have inside this loop is the anonymous function I pass to the Array.prototype.map() method :
function (item) {
return item.id;
}
Which would be useless if I define it outside my loop since it's body only contains one line of code.
Why I'm getting this error ? and how can I tell SonarQube to skip it.
How about you define it outside the loop
var mapFunction = function (item) {
return item.id;
};
for (var entry in metadata) {
if (metadata.hasOwnProperty(entry)) {
var varName = metadata[entry].variableName;
if (metadata[entry].multipleValues === "false") {
if (angular.isDefined(vm[varName]) && (vm[varName] !== null) && vm[varName].id !== null) {
filters.push(factory.buildEntry(metadata[entry].variableName, vm[varName].id, null, factory.filterOperators.textContains));
}
} else {
if (angular.isDefined(vm[varName]) && (angular.isArray(vm[varName])) && (vm[varName].length > 0)) {
filters.push(factory.buildEntry(metadata[entry].variableName, null, vm[varName].map(mapFunction), factory.filterOperators.textContains));
}
}
}
}
You can assign functions to variables and then treat that variable as a function
var foo = function(){console.log('bar')};
foo();
In your case by assigning your mapping function to a variable, then passing that variable to the .map() gives you also an efficiency boost, because the function doesn't have to be re-instantiated every single time the loop runs. It can just re-use the same function over and over.
And as soon as the enclosing function ends which executes the loop, the variable ceases to exist.

How to accomplish this without using eval

Sorry for the title but I don't know how to explain it.
The function takes an URI, eg: /foo/bar/1293. The object will, in case it exists, be stored in an object looking like {foo: { bar: { 1293: 'content...' }}}. The function iterates through the directories in the URI and checks that the path isn't undefined and meanwhile builds up a string with the code that later on gets called using eval(). The string containing the code will look something like delete memory["foo"]["bar"]["1293"]
Is there any other way I can accomplish this? Maybe store the saved content in something other than
an ordinary object?
remove : function(uri) {
if(uri == '/') {
this.flush();
return true;
}
else {
var parts = trimSlashes(uri).split('/'),
memRef = memory,
found = true,
evalCode = 'delete memory';
parts.forEach(function(dir, i) {
if( memRef[dir] !== undefined ) {
memRef = memRef[dir];
evalCode += '["'+dir+'"]';
}
else {
found = false;
return false;
}
if(i == (parts.length - 1)) {
try {
eval( evalCode );
} catch(e) {
console.log(e);
found = false;
}
}
});
return found;
}
}
No need for eval here. Just drill down like you are and delete the property at the end:
parts.forEach(function(dir, i) {
if( memRef[dir] !== undefined ) {
if(i == (parts.length - 1)) {
// delete it on the last iteration
delete memRef[dir];
} else {
// drill down
memRef = memRef[dir];
}
} else {
found = false;
return false;
}
});
You just need a helper function which takes a Array and a object and does:
function delete_helper(obj, path) {
for(var i = 0, l=path.length-1; i<l; i++) {
obj = obj[path[i]];
}
delete obj[path.length-1];
}
and instead of building up a code string, append the names to a Array and then call this instead of the eval. This code assumes that the checks to whether the path exists have already been done as they would be in that usage.

Setting variable by string name in javascript?

//window["Fluent"]["Include"]
function setGlobalVariableByName(name,value)
{
var indexes = name.split(".");
var variable = null;
$.each(indexes, function()
{
if (variable == null){
variable = window[this];
}else{
variable = variable[this];
}
});
variable = value;
}
setGlobalVariableByName("Fluent.Include.JqueryPulse",true);
console.log(Fluent.Include.JqueryPulse) // prints false
this doesn't work, obviously. It would work if I just wanted to get the variable's value, but not for setting it.
window["Fluent"]["Include"]["JqueryPulse"] = true;
console.log(Fluent.Include.JqueryPulse) // prints true
how could I achieve something like this without using eval?
I'd need some way to programmatically add array indices to this, I'd guess
The following works, can you suggest a better way to code it in order to make it more DRY?
function setGlobalVariableByName(name,value)
{
var indices = name.split(".");
var parent;
$.each(indices, function(i)
{
if(i==indices.length-1){
if (!parent){
window[this] = value;
}else{
parent[this] = value;
}
}else if (!parent){
parent = window[this];
}else{
parent = variable[this];
}
});
}
setGlobalVariableByName : function(name, value)
{
var indices = name.split(".");
var last = indices.pop();
var parent;
$.each(indices, function(i)
{
if (!parent){
parent = window[this];
}else{
parent = variable[this];
}
});
if (!parent){
window[last] = value;
}else{
parent[last] = value;
}
}
You need to call
variable[this] = value
somehow. So you need to break the loop of the splited string before reching the last name, and then assign the value.
Ultimatively you need to call:
variable = window['Fluent']['Include']; // build this in a loop
variable['JqueryPulse'] = someValue; // then call this
Ultimately you're just building an object chain and setting the final item in the chain to a value. Also, I would add a check to ensure that items which are already objects do not get overwritten so that their existing properties don't get lost:
//bootstrap the object for demonstration purposes--not necessary to make code work
window.Fluent = {
Include: {
foo: 'bar', //don't want to lose this'
JqueryPulse: false //want to set this to true
}
};
//define function
function setGlobalItemByName( name, value )
{
var names,
finalName,
//no need to figure out if this should be assigned in the loop--assign it now
currentOp = window;
if( typeof name === 'string' && name !== '' )
{
names = name.split( '.' );
//no need to track where we are in the looping--just pull the last off and use it after
finalName = names.pop();
$.each( names, function()
{
//If the current item is not an object, make it so. If it is, just leave it alone and use it
if( typeof currentOp[this] !== 'object' || currentOp[this] === null )
{
currentOp[this] = {};
}
//move the reference for the next iteration
currentOp = currentOp[this];
} );
//object chain build complete, assign final value
currentOp[finalName] = value;
}
}
//use function
setGlobalItemByName( 'Fluent.Include.JqueryPulse', true );
//Check that Fluent.Include.foo did not get lost
console.log( Fluent.Include.foo );
//Check that Fluent.Include.JqueryPulse got set
console.log( Fluent.Include.JqueryPulse );
However, I would do it without using jQuery, even if you have jQuery available on the page. There is no need for the overhead of executing a function for each index.
//bootstrap the object for demonstration purposes--not necessary to make code work
window.Fluent = {
Include: {
foo: 'bar', //don't want to lose this'
JqueryPulse: false //want to set this to true
}
};
//define function
function setGlobalItemByName( name, value )
{
var names,
finalName,
indexCount,
currentIndex,
currentName,
//no need to figure out if this should be assigned in the loop--assign it now
currentOp = window;
if( typeof name === 'string' && name !== '' )
{
names = name.split( '.' );
//no need to track where we are in the looping--just pull the last off and use it after
finalName = names.pop();
indexCount = names.length;
for( currentIndex = 0; currentIndex < indexCount; currentIndex += 1 )
{
currentName = names[currentIndex];
//If the current item is not an object, make it so. If it is, just leave it alone and use it
if( typeof currentOp[currentName] !== 'object' || currentOp[currentName] === null )
{
currentOp[currentName] = {};
}
//move the reference for the next iteration
currentOp = currentOp[currentName];
}
//object chain build complete, assign final value
currentOp[finalName] = value;
}
}
//use function
setGlobalItemByName( 'Fluent.Include.JqueryPulse', true );
//Check that Fluent.Include.foo did not get lost
console.log( Fluent.Include.foo );
//Check that Fluent.Include.JqueryPulse got set
console.log( Fluent.Include.JqueryPulse );

Defining prototype property for JavaScript for XML prototype functions

I am using custom javascript functions provided at this link (http://km0.la/js/mozXPath/) to implement particular XML functionality in FireFox.
Here is the code:
// mozXPath
// Code licensed under Creative Commons Attribution-ShareAlike License
// http://creativecommons.org/licenses/by-sa/2.5/
if( document.implementation.hasFeature("XPath", "3.0") ) {
if( typeof XMLDocument == "undefined" ) { XMLDocument = Document; }
XMLDocument.prototype.selectNodes = function(cXPathString, xNode) {
if( !xNode ) { xNode = this; }
var oNSResolver = this.createNSResolver(this.documentElement);
var aItems = this.evaluate(cXPathString, xNode, oNSResolver,
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
var aResult = [];
for( var i = 0; i < aItems.snapshotLength; i++) {
aResult[i] = aItems.snapshotItem(i);
}
return aResult;
}
XMLDocument.prototype.selectSingleNode = function(cXPathString, xNode) {
if( !xNode ) { xNode = this; }
var xItems = this.selectNodes(cXPathString, xNode);
if( xItems.length > 0 ){ return xItems[0]; }
else{ return null; }
}
Element.prototype.selectNodes = function(cXPathString) {
if(this.ownerDocument.selectNodes) {
return this.ownerDocument.selectNodes(cXPathString, this);
}
else { throw "For XML Elements Only"; }
}
Element.prototype.selectSingleNode = function(cXPathString) {
if(this.ownerDocument.selectSingleNode) {
return this.ownerDocument.selectSingleNode(cXPathString, this);
}
else { throw "For XML Elements Only"; }
}
}
Assuming the XML object has been defined and loaded with XML content, here is an example of how one would access a an XML tag named "cd_rank":
var cd_rank_XMLObj = XMLObj.selectSingleNode("cd_rank");
What I want to do is add the property "nodeTypedValue" to the selectSingleNode() function, but I'm not sure how to do this. In the Element.prototype.selectSingleNode function, I tried adding:
this.prototype.nodeTypedValue = this.textContent;
However, it's giving me an error saying it's undefined. I even tried adding it outside of the function, just to dumb it down and get the concept, and it also says it's undefined:
var XMLObj.selectSingleNode.prototype.nodeTypedValue = XMLObj.textContent;
alert(XMLObj.selectSingleNode("cd_rank").nodeTypedValue);
Essentially what I'm trying to do, I suppose, is add a prototype property to a prototype function. But I need some help with this. How can i add "nodeTypedValue" such that I write "XMLObj.selectSingleNode(Path).nodeTypedValue"?
Okay, I think I figured out how to add it inside the function, probably due more to luck than logic:
Element.prototype.selectSingleNode = function(cXPathString){
if(this.ownerDocument.selectSingleNode) {
var result = this.ownerDocument.selectSingleNode(cXPathString, this);
if (result != null) {
result.nodeTypedValue = result.textContent;
}
return result;
}
else{throw "For XML Elements Only";}
}

Categories

Resources