0

I have a function that sorts a list of lists by the first list. When I use the function with the variables like so:

sort_lists(IN[0],IN[1],IN[2])

it works perfectly. Although, as I don't know how many lists my input contains, I want to use this as my variable:

sort_lists(IN[idx] for idx in range(len(IN)))

Although this returns a sorting of one list (the superlist). Why is there a difference between these variables, and how can I improve the code?

Here is the function if decisive (here IN[0] is the input with a number of sublists):

def sort_lists(*args):
    zipped_list= zip(*sorted(zip(*args)))
    return [list(l) for l in zipped_list]

OUT = sort_lists(data_sort[0],data_sort[1],data_sort[2])

I want to use this output:

OUT = sort_lists(data_sort[idx] for idx in range(len(IN[0])))
Georgy
  • 12,464
  • 7
  • 65
  • 73
Ask
  • 27
  • 6
  • 1
    It’s the difference between arguments: `foo(a, b, c)` and one argument that’s a list: `foo([a, b, c])`. You’re already using `*args`, though – do you know what it does? – Ry- Jan 10 '19 at 09:31
  • @Ry- I know that *args enables me to have an ungiven number of arguments as my input. Although how do i use that in my output? – Ask Jan 10 '19 at 09:55
  • I am not completely sure what you try to do with your function. Can you provide an example for IN and the corresponding expected OUT? But as @Ry- already mentioned, the first time your are passing 3 separate lists, and the second time, you are basically giving a generator object as the lone argument to the function. – DocDriven Jan 10 '19 at 09:58
  • @DocDriven What I wanna do (and am able to do now with a known number of inputs) is to sort a number of lists by the first list's sorting pattern. Say, I have an IN[0]=[2,4,3,1], and an in IN[1]=["two", "four", "three", "one"]. My output is OUT=[1,2,3,4],["one","two","three","four"]. – Ask Jan 10 '19 at 10:06

3 Answers3

2

Two things to understand here:

You can see how your inputs are different if you simply add print statement in your function:

def sort_lists(*args):
    print(args)
    zipped_list= zip(*sorted(zip(*args)))
    return [list(l) for l in zipped_list]

Let the input list of lists be: lists = [[2, 1, 3], [1, 3, 4], [5, 4, 2]].

sort_lists(lists[0], lists[1], lists[2])

will print: ([2, 1, 3], [1, 3, 4], [5, 4, 2]). That's a tuple of inner lists.

Though, if you call it like this:

sort_lists(lists[idx] for idx in range(len(lists)))

or

sort_lists(sublist for sublist in lists)

this will print (<generator object <genexpr> at 0x0000007001D3FBA0>,), a one-element tuple of a generator.

You can make your function work with a generator by accepting only one parameter:

def sort_lists(arg):
    zipped_list= zip(*sorted(zip(*arg)))
    return [list(l) for l in zipped_list]

sort_lists(lists[idx] for idx in range(len(lists)))
# [[1, 2, 3], [3, 1, 4], [4, 5, 2]]

but I suggest to leave your function as is, and unpack your lists in the place where you call it instead:

>>> sort_lists(*lists)
[[1, 2, 3], [3, 1, 4], [4, 5, 2]]
Georgy
  • 12,464
  • 7
  • 65
  • 73
  • Oh wow. Thanks for the answer! I just unpacked my lists in the output and it works as I wanted it to now. – Ask Jan 10 '19 at 10:12
0

Just change the function to accept list of lists, is it problem? This piece of code:

IN[idx] for idx in range(len(IN))

returns again list of lists

0

As Georgy pointed out, the difference is between the arguments being a generator or list. I would also like to point out that this is an opportunity to use and practice the map method. map applies the same function to each entry in a list. The function can be a built-in like sorted.

list_a = [[2, 1, 3], [1, 3, 4], [5, 4, 2]]
sorted_list_a = list(map(sorted, list_a)) # sorted is the python built-in function
print(sorted_list_a)

Returns:

[[1, 2, 3], [1, 3, 4], [2, 4, 5]]

You'll notice that you'll have to pass your map to the list function because map returns a map object, so you have to turn it into a list.

The map documentation is here. And a good example of it is here.

Ollie in PGH
  • 2,559
  • 2
  • 16
  • 19