8

I have an array of bools and now I want to swap those entries for numbers.

False => 0
True => 1

I have written two different pieces of code and I would like to know, which one is better and why. This is not so much about actually solving the problem, as about learning.

arr = [[True,False],[False,True],[True,True]]

for i,row in enumerate(arr):
    for j,entry in enumerate(row):
        if entry:
            arr[i][j] = 1
        else:
            arr[i][j] = 0
print(arr)

And the second approach:

arr = [[True,False],[False,True],[True,True]]

for i in range(len(arr)):
    for j in range(len(arr[i])):
        if arr[i][j]:
            arr[i][j] = 1
        else:
            arr[i][j] = 0    
print(arr)

I read that there are ways to do this with importing itertools or similar. I am really not a fan of importing things if it can be done with “on-board tools”, but should I rather be using them for this problem?

2
  • 2
    Definitely option A; iterating over range(len(...)) isn't pythonic. Commented Oct 31, 2015 at 0:26
  • Thank you very much. That was the answer I was looking for ! Commented Oct 31, 2015 at 14:33

2 Answers 2

12

Let's define your array:

>>> arr = [[True,False],[False,True],[True,True]]

Now, let's convert the booleans to integer:

>>> [[int(i) for i in row] for row in arr]
[[1, 0], [0, 1], [1, 1]]

Alternatively, if we want to be more flexible about what gets substituted in, we can use a ternary statement:

>>> [[1 if i else 0 for i in row] for row in arr]
[[1, 0], [0, 1], [1, 1]]
Sign up to request clarification or add additional context in comments.

3 Comments

Beat me to it and much more elegant, I just did [(lambda x: [int(y) for y in x])(i) for i in foo] - I can't much say why my brain shutdown so hard and I didn't just do what you did :) end of the day on a friday must be
Maybe int(bool(i)) rather than the ternary?
John1024. Thank you very much for also adding in the last part. Because later on I might want to use some other symbols instead of 1 and 0 :D
4

If you want to stay with a for-loop (e.g. because you want to mutate the existing array instead of creating a new one), you should simplify the code.

I would first simplify the outer loop by removing the indexing (there is no need for it since it's even easier to modify a row than a nested array):

for row in arr:
    for j, entry in enumerate(row):
        if entry:
            row[j] = 1
        else:
            row[j] = 0

these kinds of simple if statement can often be simplified by using an if expression:

 row[j] = 1 if entry else 0

but in this case we can do even better. bool is a subclass of int (ie. all bool's are int's), and True and False are defined to be exactly 1 and 0 respectively -- if you scroll down to the specification section of PEP 285 (https://www.python.org/dev/peps/pep-0285/) you'll see that that equivalence is not accidental but very much by design.

We can therefore use the int constructor to grab the underlying integer values[*], since int(True) == 1 and int(False) == 0, the if-expression can be simplified to:

row[j] = int(entry)

[*] technically this is an explicit upcast to a base class, and not a conversion constructor..

The simplified code:

for row in arr:
    for j, entry in enumerate(row):
        row[j] = int(entry)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.