How to use if statement when function returns 2 arguments? - solidity

I recently created a function that checks if a pair exists in my swap smart contract.
The functoin looks like this:
function checkIfPairExists(address _token1, address _token2) internal returns(uint, bool) {
for (uint index = 0; index < tokenPairs.length; index++) {
if (tokenPairs[index].token1 == _token1 && tokenPairs[index].token2 == _token2) {
return (index, true);
}
else if (tokenPairs[index].token2 == _token1 && tokenPairs[index].token1 == _token2) {
return (index, true);
} else {
return (0, false);
}
}
}
This function works fine but then when I try to use the function in an if statement like this:
if (checkIfPairExists(_token1, _token2) == (uint256, true))
How do I write it so it is correct? I am trying to receive index of the pair for my array and bool to see if the pair exists. I then need to save that index to find which pair it should add to.
Hope it makes sense.
Let me know if I should rephrase the question so more people will understand it and it can help them.
Thanks

You need to assign the returned values to two separate variables. Then you can validate either of them.
(uint256 index, bool exists) = checkIfPairExists(_token1, _token2);
if (exists == true) {
// do something with `index`
}

As said in the above answer by #pert-hejda, you will need to assign the function return values then you can use those to check the condition. Why? Because multiple returns are represented as tuples and currently the feature you want is not supported in solidity. So, you will need to assign the return values and use those values in conditionals. Thank you.

Related

Creating single use intermediate variables

I've read somewhere that a variable should be entered into the code if it is reused. But when I write my code for logic transparency, I sometimes create intermediate variables (with names reflecting what they contain) which are used only once.
How incorrect is this concept?
PS:
I want to do it right.
It is important to note that most of the time clarity takes precedence over re-usability or brevity. This is one of the basic principles of clean code. Most modern compilers optimize code anyway so creating new variables need not be a concern at all.
It is perfectly fine to create a new variable if it would add clarity to your code. Make sure to give it a meaningful name. Consider the following function:
public static boolean isLeapYear(final int yyyy) {
if ((yyyy % 4) != 0) {
return false;
}
else if ((yyyy % 400) == 0) {
return true;
}
else if ((yyyy % 100) == 0) {
return false;
}
else {
return true;
}
}
Even though the boolean expressions are used only once, they may confuse the reader of the code. We can rewrite it as follows
public static boolean isLeapYear(int year) {
boolean fourth = year % 4 == 0;
boolean hundredth = year % 100 == 0;
boolean fourHundredth = year % 400 == 0;
return fourth && (!hundredth || fourHundredth);
}
These boolean variables add much more clarity to the code.
This example is from the Clean Code book by Robert C. Martin.

How to avoid !! in a function which returns a non-nullable

In the sample below, the function should return a non-null data.
Since the data could be changed in the process, it needs to be var, and can only be nullable to start with.
I can't use lateinit because the first call of if (d == null) will throw.
After the process it will be assigned a non-null data, but the return has to use the !! (double bang or non-null assertion operator).
What is the best approach to avoid the !!?
fun testGetLowest (dataArray: List<Data>) : Data {
var d: Data? = null
for (i in dataArray.indecs) {
if (d == null) {// first run
d = dataArray[i]
} else if {
d.level < dataArray[i].level
d = dataArray[i]
}
}
return d!!
}
If you don't like !! then supply a default value for it. You'll realize you can only supply the default value if the list is not empty, but, as you said, the list is already known to be non-empty. The good part of this story is that the type system doesn't track list size so when you say dataArray[0], it will take your word for it.
fun testGetLowest(dataArray: List<Data>) : Data {
var d: Data = dataArray[0]
for (i in 1 until dataArray.size) {
if (d.level < dataArray[i].level) {
d = dataArray[i]
}
}
return d
}
Normally, you can and should lean on the compiler to infer nullability. This is not always possible, and in the contrived example if the inner loop runs but once d is non-null. This is guaranteed to happen if dataArray has at least one member.
Using this knowledge you could refactor the code slightly using require to check the arguments (for at least one member of the array) and checkNotNull to assert the state of the dataArray as a post-condition.
fun testGetLowest (dataArray: List<Data>) : Data {
require(dataArray.size > 0, { "Expected dataArray to have size of at least 1: $dataArray")
var d: Data? = null
for (i in dataArray.indecs) {
if (d == null) {// first run
d = dataArray[i]
} else if {
d.level < dataArray[i].level
d = dataArray[i]
}
}
return checkNotNull(d, { "Expected d to be non-null through dataArray having at least one element and d being assigned in first iteration of loop" })
}
Remember you can return the result of a checkNotNull (and similar operators):
val checkedD = checkNotNull(d)
See Google Guava's Preconditions for something similar.
Even if you were to convert it to an Option, you would still have to deal with the case when dataArray is empty and so the value returned is undefined.
If you wanted to make this a complete function instead of throwing an exception, you can return an Option<Data> instead of a Data so that the case of an empty dataArray would return a None and leave it up to the caller to deal with how to handle the sad path.
How to do the same check, and cover the empty case
fun testGetLowest(dataArray: List<Data>)
= dataArray.minBy { it.level } ?: throw AssertionError("List was empty")
This uses the ?: operator to either get the minimum, or if the minimum is null (the list is empty) throws an error instead.
The accepted answer is completly fine but just to mentioned another way to solve your problem by changing one line in your code: return d ?: dataArray[0]

Operator '==' cant be applied to 'Boolean' and 'Char'

So i want to compare three members of an array with as little code as possible. Heres what i did:
for(i in 0..2) {
if(board[i][0] == board[i][1] == board[i][2]) {
return true
} else if(board[0][i] == board[1][i] == board[2][i]) {
return true
}
}
(All of the values ar Char's FYI) But it didnt work. I get this error message "Operator '==' cant be applied to 'Boolean' and 'Char'". I also tried using .equals, but that just didnt work. Any ideas on what to do?
You can write a small function to keep it more readable and tidy, especially if You need to do that comparison often:
fun allEqual(vararg items: Any) : Boolean {
for(i in 1..(items.size-1)){
if(items[0] != items[i]) return false
}
return true
}
And invoke simply by comma separating values:
allEqual(board[i][0], board[i][1], board[i][2])
I don't know Kotlin specifically, but most* languages don't allow you to compare 3 values at the same time. What your error message is communicating is that your code ends up comparing
"Is board[i][0] equal to board[i][1]?" which is true/false (Boolean)
to
board[i][2], which is a Char.
*I don't know of any, but maybe there's one out there that does.
You have included this condition:
if(board[i][0] == board[i][1] == board[i][2])
Firstly, this one is compared: board[i][1] == board[i][2]
After comparing, it returns true. After that if logic converts to:
if(board[i][0] == true)
Now, board[i][0] is a char and you are trying to compare it to a boolean which is not possible. That's why you are getting this error.
You have to change the logic to:
if((board[i][0] == board[i][1]) && (board[i][1] == board[i][2]))
So, your code will be:
for(i in 0..2) {
if((board[i][0] == board[i][1]) && (board[i][1] == board[i][2])) {
return true
} else if((board[0][i] == board[1][i]) && (board[1][i] == board[2][i])) {
return true
}
}
Another approach:
for (i in 0..2) {
if (board[i].toSet().size == 1)
return true
else if (board.map { it[i] }.toSet().size == 1)
return true
}
As the others said, your first comparison returns Boolean, and the second compares Boolean to Char.
You can use an extension function, and transitivity to simplify things:
fun Any.equalsAll(vararg others: Any):Boolean
{
others.forEach {
if(it!=this)
return false
}
return true
}
and call:
if (board[0][i].equalsAll(board[1][i], board[2][i]))

Comparing two BOOL values

In my instance method, would like to compare a BOOL parameter with the content of a static variable, for instance:
- (NSArray*)myMethod:(NSString*)someString actualValuesOnly:(BOOL)actualValuesOnly {
static NSString *prevSsomeString;
static BOOL prevActualValuesOnly;
static NSArray *prevResults
if ([someString isEqualToString:prevSomeString] &&
([actualValuesOnly isEqual: prevActualValuesOnly])
// HOW TO COMPARE THESE TWO BOOLEANS CORRECTLY??
{ return prevResults; }// parameters have not changed, return previous results
else { } // do calculations and store parameters and results for future comparisons)
What would be the correct way to do this?
Since BOOL is a primitive (or scalar) type, and not a class, you can compare it directly with ==
if ([someString isEqualToString:prevSomeString] && actualValuesOnly == prevActualValuesOnly)
Boolean variable is compare with == sign instead of isEqual
if(Bool1 == Bool2){
// do something here}
Boolean is compare with == sign instead of isequal:
The solutions mentioned here are not the safest way to compare 2 BOOL values, because a BOOL is really just an integer, so they can contain more than just YES/NO values. The best way is to XOR them together, like detailed here: https://stackoverflow.com/a/11135879/1026573
As Matthias Bauch suggests,
Simply do the comparison using == operator i.e
if (BOOL1 == BOOL2)
{
//enter code here
}

"used struct type value where scalar is required" at .layer.position

I want to make a selection before apply one of two animations,
what I thought is: make a Point one, if my myImageView is at the Point one, then apply animationNo1, else apply animationNo2, but I got this:"used struct type value where scalar is required", at line if (myImageView.layer.position = one)
What I do? how can I fix this?
Does anyone know exactly what makes the problem happen?
CGPoint one = CGPointMake(myImageView.layer.position.x, 100);
if (myImageView.layer.position = one)
{
animationNo1
}
else
{
animationNo2
}
First of all, your if-statement will not do what you think. If you want to compare something you have to use == (ie 2 =)
and you can't compare CGPoints like this.
use
if (CGPointEqualToPoint(one, self.view.layer.position))
if (myImageView.layer.position = one) { animationNo1 }
should be
if (CGPointIsEqualToPoint(myImageView.layer.position, one)) { animationNo1 }
You used a single = meaning assignment, rather than a == for comparison. But the == wouldn't do what you wanted here anyway.
You are passing a struct (int this case position) instead of a scalar. To do what you want you need to use CGPointIsEqualToPoint:
if (CGPointEqualToPoint(one, self.view.layer.position))
Full code with corrections:
CGPoint one = CGPointMake(myImageView.layer.position.x, 100);
if (CGPointEqualToPoint(one, self.view.layer.position))
{
animationNo1
}
else
{
animationNo2
}
Also, as others have pointed out: Be careful about = vs ==. They are different. In this case you don't use == for comparison fortunately, but if you use = for other stuff it will make it true instead of checking to see if it is true.