[Tutor] combining tuples in a list

Lloyd Hugh Allen lha2@columbia.edu
Fri, 05 Apr 2002 13:22:11 -0500


Brad Reisfeld wrote:
> 
> Hi,
> I am faced with the problem of 'combining' tuples in a list in such a way
> that any tuple whose last element matches the first element of another tuple
> must be combined (eliminating the duplicate element). My aim is to end up
> with a list of these resulting tuples.
> 
> In practice, I have a list containing a very large number of tuples (each of
> which has three items). A short example illustrating this is
> 
> inlist =
> [('c','p','l'),
> ('d','e','f'),
> ('c','h','i'),
> ('f','w','x'),
> ('a','b','c'),
> ('g','h','i'),
> ('r','z','d')]
> 
> For this case we see that the last element in the fifth tuple, 'c', matches
> the first element in the third tuple. Combining these (and eliminating the
> duplicate match) gives ('a','b','c') '+' ('c','h','i') =>
> ('a','b','c','h','i')
> 
> Following this procedure exhaustively on the list above would yield the
> desired result (order of tuples in the final list doesn't matter):
> 
> outlist =
> [('a','b','c','h','i'),
> ('r','z','d','e','f','w','x'),
> ('g','h','i'),
> ('a','b','c','p','l')]
> 
> Currently, I am carrying out this procedure in a seemingly very inefficient
> way by going through the list of tuples multiple times for each tuple I am
> building up. I'm sure there must be a better way.
> 
> Any suggestions are appreciated.
> 
> Thanks.
> 
> -Brad
> 
> _______________________________________________
> Tutor maillist  -  Tutor@python.org
> http://mail.python.org/mailman/listinfo/tutor

Here's an ugly way--the idea is that it runs through the list twice
(once forwards, then once through the output the other direction), and
hopefully gets everything that way. I'm uncomfortable with the repeated
output that is created from the (abc)(cpl)(chi), but that seems to be
what you want. The problem is that (aba)(aca) should, following these
rules, create an infinite loop (I'm assuming that an item should not
join to itself).

Because what we want the tuples to do to each other is extremely similar
to tuple concatenation, I thought it would be a good chance to use class
inheritance; __mul__ is overwritten instead of __add__ because the
procedure refers to the old __add__, if that makes any sense. That whole
"don't use the word in its own definition" (usually) thing.

###
class concatuple(tuple):
    def __mul__(self, other):
        """concatenates two tuples together, dropping the common
element.

        Ugly stuff happened when I tried to use __add__ to do this (inf
loop;
        sorry for the kludge)."""
#        print self
#        print other
        if self[-1] == other[0]:
            return self + other[1:]
        else:
            raise ValueError

def cyclelist(inlist):
    outlist = []
    while inlist:
        first = inlist[0]
    #    print first
        for second in inlist[1:]:
            if first[-1] == second[0]:
                inlist.append(first*second)
                try:
                    inlist.remove(first)
                except:
                    print "First arg tried to be removed twice"
                try:
                    inlist.remove(second)
                except:
                    print "Second arg tried to be removed twice"

        if first == inlist[0]:  #no matches found
            outlist.append(first)
            inlist.remove(first)
    return outlist

        
if __name__ == "__main__":
    inlist = map(concatuple, [('c','p','l'),
              ('d','e','f'),
              ('c','h','i'),
              ('f','w','x'),
              ('a','b','c'),
              ('g','h','i'),
              ('r','z','d')])
    newin = cyclelist(inlist)
#    print newin
    newin.reverse()
#    print newin
    outlist = cyclelist(newin)
    print outlist