0

I have a tuple, which gives me something like this:

(array([115, 115, 295], dtype=int64), array([377, 378,404], dtype=int64))

Now I want to convert it as a set like this:

{(115,377),(115,378),(295,404)}

I'm currently using this approach :

def edges2cordinate(edges):
    l = int(len(edges[0]))
    res=set()
    for i in range (0,len):
        res.add((edges[0][i],edges[1][i]))
    return res

Code link

But gives me error.

Traceback (most recent call last): File "F:\canny_vertices.py", line 40, in print(edges2cordinate(indices)) File "F:\canny_vertices.py", line 16, in edges2cordinate for i in range (0,len): TypeError: 'builtin_function_or_method' object cannot be interpreted as an integer

What am I missing ?

glibdud
  • 7,550
  • 4
  • 27
  • 37

4 Answers4

2

In your previous code, you would want to do range (0,l) instead of range (0,len), since you have saved your length variable in l, so you want to pass that length to the range variable instead of inbuilt function len

Once you do that, the original code works fine


def edges2cordinate(edges):
    l = int(len(edges[0]))
    res=set()
    for i in range (0,l):
        res.add((edges[0][i],edges[1][i]))
    return res

print(edges2cordinate([[115, 115, 295],[377, 378,404]]))

The output will be {(295, 404), (115, 378), (115, 377)}

But this can also be easily done via zip, where we unpack the list of lists, pass it to zip as an argument, and then convert the zipped iterator back to set, note that we see an unordered output here due to set

li = [[115, 115, 295],[377, 378,404]]

print(set(zip(*li)))

Output will be {(295, 404), (115, 378), (115, 377)}

To reverse the items, just reverse them when creating the set via list-comprehension by reversing individual elements of the tuple!

li = [[115, 115, 295],[377, 378,404]]

print(set((y,x) for x, y in zip(*li)))

The output will be {(404, 295), (377, 115), (378, 115)}

Devesh Kumar Singh
  • 20,259
  • 5
  • 21
  • 40
  • 1
    Given that the OP is errouneously passing the `len` function, to range, perhaps you should consider not just giving them a one-liner which they likely won't even understand or at least explain what they did wrong and how your solution works – juanpa.arrivillaga May 09 '19 at 17:03
  • Fixed @juanpa.arrivillaga please have a look and see if looks better :) – Devesh Kumar Singh May 09 '19 at 17:07
  • How can I reverse each one of them, like from `(295, 404)` to `(404, 295)` ? –  May 09 '19 at 17:09
1

Use zip with unpacking:

list(zip(*tupl))  # where tupl is your tuple

which outputs a list of values as oppose to your expectation. But I guess this is probably more suited as set will remove duplicates and has no order.

Austin
  • 25,759
  • 4
  • 25
  • 48
0
def edges2cordinate(edges):
    res = set()
    for i in range(len(edges[0])):
        res.add((edges[0][i], edges[1][i]))
    return res

edge = ([115, 123, 295], [377, 378, 404])
res = edges2cordinate(edge)
set(reversed(list(res)))

This also gives the correct output {(115, 377), (115, 378), (295, 404)}, which is a simpler solution if you are not familiar with zip. The reason your code did not work properly was correctly answered above.

ipol
  • 1
  • 3
0

to explain how to get between all of the other answers, we start with a tidied up (and fixed) version of the OPs original code:

def edges2cordinate(edges):
    num_entries = len(edges[0])
    res = set()
    for i in range(0, num_entries):
        res.add((edges[0][i], edges[1][i]))
    return res

the first cleanup happens after noticing that range() allows a single argument to be passed, causing it to count from zero up to that argument. we also notice that num_entries is only used once, so we can move it to where it's used, giing us:

def edges2cordinate(edges):
    res = set()
    for i in range(len(edges[0])):
        res.add((edges[0][i], edges[1][i]))
    return res

second, notice that we keep getting the first and second elements of edges on every iteration of the for loop. Python gives us "assignment expressions" (PEP 572) which lets us write this as:

def edges2cordinate(edges):
    first_arr, second_arr = edges
    res = set()
    for i in range(len(first)):
        res.add((first_arr[i], second_arr[i]))
    return res

and should be a bit faster, because we only need to get the items out once rather than every loop iteration.

every answer has pointed to using zip() to make this code more concise. you use zip by giving it multiple things that can be iterated over, like lists or your arrays, and it zips them giving you back tuples containing items from the things you gave it. e.g:

list(zip([1,2,3], [4,5,6]))

evaluates to [(1,4), (2,5), (3,6)]. the reason for the list() surrounding it is that (in Python 3) zip gives you back an iterator, and passing this iterator to list() gives you a list containing items from that iterator.

third, the smallest change to use zip on the above code would be:

def edges2cordinate(edges):
    first_arr, second_arr = edges
    res = set()
    for first_item_i, second_item_i in zip(first_arr, second_arr):
        res.add((first_item_i, second_item_i))
    return res

further, we're using these "assignment expressions" to redundantly take things apart and put them together, so we can rewrite this to:

def edges2cordinate(edges):
    res = set()
    for pair_i in zip(edges[0], edges[1]):
        res.add(pair_i)
    return res

we can also change to using zip(*edges) which "unpacks" edges into the arguments to zip, i.e. edges[0] goes to the first parameter of zip, edges[1] goes to the second parameter, etc.

another tool Python gives you are generator expressions, which lets us turn lots of this into one line:

def edges2cordinate(edges):
    return set(pair_i for pair_i in zip(*edges))

but we aren't "doing anything" with this generator expression, so it can be removed, getting to the minimalist:

def edges2cordinate(edges):
    return set(zip(*edges))

in a follow-up comment, you asked how to reverse the order of the edges. one way would be to do:

def edges2cordinate(edges):
    return set(zip(edges[1], edges[0]))

which will hopefully do the obvious thing. the suggested reversed() function probably does what you'd expect, but can be used as:

def edges2cordinate(edges):
    return set(zip(*reversed(edges)))

to keep it minimal

Sam Mason
  • 15,216
  • 1
  • 41
  • 60