mama
mama

Reputation: 2227

How can I do groupby without importing anything and in an expressive and oneliner kind of way?

I am wondering if it is possible to do grouping of lists in python in a simple way without importing any libraries.

and example of input could be:

a="0 0 1 2 1"

and output

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

I am thinking something like the implementation of itertools groupby

class groupby:
    # [k for k, g in groupby('AAAABBBCCDAABBB')] --> A B C D A B
    # [list(g) for k, g in groupby('AAAABBBCCD')] --> AAAA BBB CC D
    def __init__(self, iterable, key=None):
        if key is None:
            key = lambda x: x
        self.keyfunc = key
        self.it = iter(iterable)
        self.tgtkey = self.currkey = self.currvalue = object()
    def __iter__(self):
        return self
    def __next__(self):
        self.id = object()
        while self.currkey == self.tgtkey:
            self.currvalue = next(self.it)    # Exit on StopIteration
            self.currkey = self.keyfunc(self.currvalue)
        self.tgtkey = self.currkey
        return (self.currkey, self._grouper(self.tgtkey, self.id))
    def _grouper(self, tgtkey, id):
        while self.id is id and self.currkey == tgtkey:
            yield self.currvalue
            try:
                self.currvalue = next(self.it)
            except StopIteration:
                return
            self.currkey = self.keyfunc(self.currvalue)

but shorter

Upvotes: 0

Views: 165

Answers (3)

Alain T.
Alain T.

Reputation: 42133

To group the items by value (as opposed to grouping by changes as itertools groupby would do), you can use a list comprehension that embeds a dictionary to organize the data into lists:

a="0 0 1 2 1"

groups = [ g[v] for g in [dict()] for v in map(int,a.split()) 
           if g.setdefault(v,[]).append(v) or len(g[v])==1  ]

print(groups)

[[0, 0], [1, 1], [2]]

Each list (value in the internal (g) dictionary) is augmented with the items in a but only returned once (when encountering a new grouping key).

Using a dictionary to group the items avoids the need to sort the items (which would be required if using itertools.groupby)

If you want the grouping to work exactly like groupby, then the internal dictionary needs to be cleared when a new key is encountered:

groups = [ g.setdefault(v,g.clear() or [v])
           for g in [dict()] for v in map(int,a.split()) 
           if v not in g or g[v].append(v) ]
print(groups)

[[0, 0], [1], [2], [1]]

which you can expand to produce the same output as groupby (including computed grouping keys and working from an iterable:

a="0 0 1 2 1"

ia  = map(int,a.split()) # an iterable
key = lambda v: v > 0    # a grouping key function
          
groups = [ (k,g.setdefault(k,g.clear() or [v]))
           for g in [dict()] for v in ia for k in [key(v)]
           if k not in g or g[k].append(v) ]

print(groups)
[(False, [0, 0]), (True, [1, 2, 1])]

Note that all the above solutions produce lists of grouped items (as opposed to grouby's item iterators

Upvotes: 1

mama
mama

Reputation: 2227

I just realized how to do it.

{k:[v for v in a.split() if v == k] for k in set(a.split())}

or

[tuple(v for v in a.split() if v == k) for k in set(a.split())]

however - this solution can not be used on dictionaties because dicts are not hashable - so maybe thats one of the reasons why the itertools implementation is so much more complicated.

Upvotes: 0

Jess
Jess

Reputation: 1

How about using a.split(' ') then using for loop to do that?

Upvotes: 0

Related Questions