How to store some elements from a list into another

Peter Otten __peter__ at web.de
Tue Jun 13 06:00:30 EDT 2017


Jussi Piitulainen wrote:

> breamoreboy at gmail.com writes:
> 
>> On Monday, June 12, 2017 at 7:33:03 PM UTC+1, José Manuel Suárez Sierra
>> wrote:
>>> Hello,
>>> I am stuck with a (perhaps) easy problem, I hope someone can help me:
>>> 
>>> My problem is:
>>> I have a list of lists like this one:
>>> [[55, 56, 57, 58, 83, 84, 85, 86, 89, 90, 91, 92, 107, 108, 109, 110,
>>> 111, 117, 118, 119, 120, 128, 129, 130, 131, 135, 136, 137, 138, 184,
>>> 185, 186, 187, 216, 217, 218, 219, 232, 233, 234, 235, 236, 237, 238,
>>> 267, 268, 269, 270, 272, 273, 274, 275], [2, 3, 4, 5, 9, 10, 11, 12,
>>> 21, 22, 23, 24, 29, 30, 31, 32, 56, 57, 58, 59, 65, 66, 67, 68, 74,
>>> 75, 76, 77, 78, 89, 90, 91, 92, 98, 99, 100, 101, 102, 125, 126, 127,
>>> 128, 131, 132, 133, 134, 135]]
>>> 
>>> And what I want is to store some of these datum into another list
>>> according to the next conditions:
>>>
>>> 1. I just want to store data if these values are consecutive (four in
>>> four), for instance, for first element I would like to store into the
>>> new list: [[[55,58],[83,86],....[n,n+3]]] and so on.
>>>
>>>  I tried something like this:
>>> 
>>>         x=0
>>>         y=0
>>>         while list6[x][y] == list6[x][y+1]-1 and list6[x][y] ==
>>>         list6[x][y+1]-2 and list6[x][y] == list6[x][y+1]-3 or
>>>         list6[x][0]:
>>> 
>>>             list7.append(list6[x][y])
>>>             list7.append(list6[x][y+3])
>>>             y = y+1
>>>             if (list6[x][y])%(list6[x][len(list6[x])-1]) == 0:
>>>                 x= x+1
>>> 
>>>             if len(list6[x]) == x and len(list6[x][y]) == y:
>>>                 break
>>> 
>>> 
>>> It does not work
>>> 
>>> I appreciate your help
>>> Thank you
>>
>> Perhaps use the recipe for consecutive numbers from here
>> https://docs.python.org/2.6/library/itertools.html#examples It will
>> have to be modified for Python 3, I'll leave that as an exercise.
> 
> What a clever idea. Pity it's gone in newer documentation. (By the "it"
> in the previous sentence I refer only to the idea of grouping by the
> difference to the index in the original sequence, and by "gone" only to
> the fact that I didn't see this example at the corresponding location
> for Python 3.6, which I found by replacing the 2 in the URL with 3.
> Perhaps the idea is preserved somewhere else?)
> 
> Anyway, I've adapted it to Python 3, and to an analysis of the problem
> at hand - mainly the problem that the OP finds themselves _stuck_ with
> their spec and their code, as quoted above. Hope it helps.
> 
> What follows, follows.
> 
> # The big idea is to define (auxiliary) functions. It's not an
> # advanced idea. It's the most basic of all ideas. The experience of
> # being stuck comes from trying to see the whole problem at once.
> 
> # Ok, familiary with standard ways of viewing things helps. But that
> # is just the flip side of breaking problems into manageable parts:
> # past experience suggests that some kinds of parts are more useful,
> # more composable into a solution, so in standard libraries.
> 
> # One subproblem is to group just one list of numbers, then it is easy
> # to group every list in a list of such lists. But another subproblem
> # is to deal with one group of numbers. There seem to be two concerns:
> # a group should consist of consecutive numbers, and a group should
> # consist of four numbers - the latter at least is easy enough if the
> # group is stored as a list, but what should be done if there are five
> # or seven numbers? No idea, but that can be clarified later once the
> # components of a solution are untangled into their own functions.
> 
> # The battle cry is: Use def!
> 
> import itertools as it
> import operator as op
> 
> def applied(f):
>     '''Reification of that asterisk - like a really advanced
>     computer-sciency kind of thing. But see no lambda!'''
>     def F(args): return f(*args)
>     return F
> 
> def consequences(data):
>     '''Lists of consecutive datami, clever idea adapted from
>     https://docs.python.org/2.6/library/itertools.html#examples'''
>     for k, group in it.groupby(enumerate(data), applied(op.sub)):
>         yield [datum for index, datum in group]

Hm, the itertools users' code of honour requires that no intermediate 
sequences be materialised ;) So:

second = op.itemgetter(1)

def consequences(data):
    for k, group in it.groupby(enumerate(data), applied(op.sub)):
        yield edges(map(second, group))

def isfourlong(pair):
    min, max = pair
    return max - min == 3

def edges(items):
    first = last = next(items)
    for last in items:
        pass
    return [first, last]

def ends(sequences):
    return list(sequences)

However, this is infested with for loops. Therefore

def consequences(data):
    groups = map(second, it.groupby(enumerate(data), applied(op.sub)))
    sans_index = map(functools.partial(map, second), groups)
    return map(edges, sans_index)

and because we want to hide traces that this was written by mere mortals

def consequences(data):
    return map(
        edges,
        map(
            functools.partial(map, second),
            map(second, it.groupby(enumerate(data), applied(op.sub)))
        )
    )

I don't immediately see what to do about the for loop in edges(), so I'll 
use the traditional cop-out: Removing the last loop is left as an 
exercise...

> def isfourlong(sequence):
>     '''True if sequence is of length 4.'''
>     return len(sequence) == 4
> 
> def ends(sequences):
>     '''Collect the endpoints of sequences in a list of 2-lists.'''
>     return [[sequence[0], sequence[-1]] for sequence in sequences]
> 
> data = [[55, 56, 57, 58, 83, 84, 85, 86, 89, 90, 91, 92, 107, 108,
>          109, 110, 111, 117, 118, 119, 120, 128, 129, 130, 131, 135,
>          136, 137, 138, 184, 185, 186, 187, 216, 217, 218, 219, 232,
>          233, 234, 235, 236, 237, 238, 267, 268, 269, 270, 272, 273,
>          274, 275],
>         
>         [2, 3, 4, 5, 9, 10, 11, 12, 21, 22, 23, 24, 29, 30, 31, 32,
>          56, 57, 58, 59, 65, 66, 67, 68, 74, 75, 76, 77, 78, 89, 90,
>          91, 92, 98, 99, 100, 101, 102, 125, 126, 127, 128, 131, 132,
>          133, 134, 135]]
> 
> def testrun():
>     '''See how many variations can be composed out of the few auxiliary
>     functions - the problem becomes tame, or at least a bit tamer.
>     This kind of ad-hoc test suite is very useful, during development,
>     when at all in doubt.'''
>     print('groups in both lists:')
>     print(list(consequences(data[0])))
>     print(list(consequences(data[1])))
>     # note the calls to list() - consequences() returns an opaque
>     # generator object that can be drained into a list when needed;
>     # filter objects below are similarly opaque and drainable - define
>     # an auxiliary that returns a list instead if that feels scary -
>     # Don't Panic!
>     print()
>     print('groups of four in both lists:')
>     print(list(filter(isfourlong, consequences(data[0]))))
>     print(list(filter(isfourlong, consequences(data[1]))))
>     print()
>     print('ends of all groups in both lists:')
>     print(ends(consequences(data[0])))
>     print(ends(consequences(data[1])))
>     print()
>     print('ends of groups of four in both lists:')
>     print(ends(filter(isfourlong, consequences(data[0]))))
>     print(ends(filter(isfourlong, consequences(data[1]))))
>     print()
>     print('lists of ends of all groups:')
>     print([ends(consequences(datami)) for datami in data])
>     print()
>     print('lists of ends of groups of four:')
>     print([ends(filter(isfourlong, consequences(datami)))
>            for datami in data])
> 
> if __name__ == '__main__':
>     # inside the above condition, these commands are executed when the
>     # script is executed but not when it is imported as a library, so
>     # it is easy to run the test from the shell and easy to import the
>     # definitions into an interactive session
>     testrun()

PS: Users who did not like my suggestions above may like the alternative 
shown below:

def compress(values):
    """
    >>> list(compress([]))
    []
    >>> list(compress([1]))
    [(1, 1)]
    >>> list(compress([1, 2]))
    [(1, 2)]
    >>> list(compress([1, 2, 3]))
    [(1, 3)]
    >>> list(compress([1, 2, 3, 7, 8]))
    [(1, 3), (7, 8)]
    >>> list(compress([1, 2, 3, 7, 9, 10]))
    [(1, 3), (7, 7), (9, 10)]

    >>> list(compress([3, 4, 5, 1, 2, 3, 10, 11]))
    [(3, 5), (1, 3), (10, 11)]
    >>> list(compress([1, 1]))
    [(1, 1), (1, 1)]
    >>> list(compress([1,1,2,2,3,4,7,7]))
    [(1, 1), (1, 2), (2, 4), (7, 7), (7, 7)]
    """
    values = iter(values)
    try:
        first = prev = value = next(values)
    except StopIteration:
        return
    for value in values:
        if value - prev != 1:
            yield first, prev
            first = value
        prev = value
    yield first, value





More information about the Python-list mailing list