All permutations from 2 lists

Avi Gross avigross at verizon.net
Wed Mar 2 21:42:00 EST 2022


Larry,

i waited patiently to see what others will write and perhaps see if you explain better what you need. You seem to gleefully swat down anything offered. So I am not tempted to engage.

Some later messages suggest you may not be specifying quite what you want. It sounds like you are asking for something like a function that combines two things a certain way. Initially it sounded like two simple lists of any length and something like this was proposed:

[(f, s) for f in os for s in region]

It returns a list of tuples, but could just as easily return a list of lists or dictionary or  other variants.

You pointed out, validly, that if either of the original lists is empty, it fails. True. 

But that opens the question of what happens when any random thing is thrown there such as "hello" which Python happily treats as ['h', 'e', 'l', 'l', 'o'] for this purpose. You now seem to want a bulletproof solution that might start with a very basic function like this but add more checks and deal appropriately:

def combine_2(first, second):
    l_first = len(first)
    l_second =  len(second)

    if ( l_first + l_second == 0 ): return([])
    if ( l_first == 0 ): return(second)
    if ( l_second == 0 ): return(first)
    return([(f, s) for f in first for s in second])

The above works fine if I give it [] for one or both but maybe not. It returns an empty list if both are empty, which may be what you want or you may want an error or an empty tuple, ...

You wanted tuples in return. But if either one is empty, the above returns the original, a list without tuples. To return the valid list with each element as a singleton tuple could easily be done but you have not initially specified the expected behavior.

And it is hard to guess as it is not clear what you will do with this. Do you want to be able to do this for more than 2 at a time? At some point, the above approach is not right and an approach that works for an arbitrary number of lists to combine may make sense. Some python modules do something similar to the R function expand.grid() that makes all combinations into rows of a data.frame such as this expand_grid in the pandas module: 

https://pandas.pydata.org/pandas-docs/version/0.17.1/cookbook.html#creating-example-data

I assume that could be used as-is in some applications but could also be easily converted back into a list of tuples or have a generator deliver one result each time ...

And, yes, the above approach needs to convert your arguments into a single dictionary but that is easy enough. Getting back a list of lists horizontally may be something like this:

 df.values.tolist()

As others have pointed out, you are actually not stating what you want and are getting the right answer if you ALLOW any of your items to be empty. It is like any operation using an NaN or an Inf do not work well as the result is generally nonsense. If you do not want any empty sets, take care to exclude them. In my dictionary example above, you might add only non-empty lists to the dictionary, for example, and if the dictionary remains empty, then there is nothing to combine.

I also see a subtle requirement that is annoying. You want the NAME of each entry preserved in order even as some may be EMPTY. So i ask where these names are stored so you can communicate them. This is trivial in languages like R but perhaps not in Python without some help. My suggestion is that numpy/pandas and their data types can help. Once you make a structure like I describe above, the columns have headers you can ask about and then you can pass the headers of surviving entities that perfectly match the columns, even if you take the columns out into a list of tuples.

It looks to some like you want lots of things without being willing to earn them. Any number of methods will easily remove or filter what you keep. Once you have clean data, the remainder can be trivial.

You then supplied a solution you said worked fine that looked pretty much NOTHING like what we have been discussing:

def query_lfixer(query):
    for k, v in query.items():
        if type(v)==list:
            query[k] = {"$in": v}
    return query

self._db_conn[collection_name].find(query_lfixer(query))


The above code seems be taking a dictionary and modifying it? And what you needed in the end was a dictionary? So why did so many of us bother? I will keep that in mind for future questions.


More information about the Python-list mailing list