-4

I was doing some work with python iterable parameters.

I was making a function something like this :

def once_in_a_row(iterable):
  pass

This function should take any iterable: It should produce every value in the iterable, but does not produce the same value twice in a row: if the current value is the same as the last one yielded, it skips yielding the current value.

Example :

for i in once_in_a_row('abbcccaadd'):
  print(i,end=' ')

It produces the values 'a', 'b', 'c', 'a', and 'd'.

What can be best simple way to do it ? I am having a hide(iterable) definition too.

def hide(iterable):
  for v in iterable:
    yield v

This function is called to ensure that code works on general iterable parameters (not just a string, tuple, list, etc.). For example, although we can call len(string) we cannot call len(hide(string)), so the function once_in_a_row should not call len on their parameters

Claude
  • 8,806
  • 4
  • 41
  • 56
  • 1
    This sounds like homework. You should show us what you have tried so far. And I don't understand what the `hide` function is for. – Daniel Roseman Apr 27 '15 at 08:32
  • @DanielRoseman: I believe `hide` came from his teacher, and it's there to prevent his `once_in_row` function from finding out anything about the iterable other than that it's an iterable (in particular, to explicitly break code that relies on the input data being sequences). At least I think that's what the last paragraph is trying to say. Of course you could just write that as `hide = iter`, which does the same thing but more efficiently and more obviously. :) – abarnert Apr 27 '15 at 09:35

3 Answers3

2

If by "simplest" you mean "most novice-friendly" or "least abstract", just keep track of the last value and skip it:

def once_in_row(iterable):
    last_value = object() # won't match anything
    for value in iterable:
        if value != last_value:
            yield value
        last_value = value

But if by "simplest" you mean "fewest things to keep track of", there's an even simpler way: use groupby to group the values into runs, then just print one value for each run. In fact, this is useful enough that it's included in the itertools recipes as unique_justseen:

from itertools import groupby
from operator import itemgetter

def unique_justseen(iterable, key=None):
    "List unique elements, preserving order. Remember only the element just seen."
    # unique_justseen('AAAABBBCCDAABBB') --> A B C D A B
    # unique_justseen('ABBCcAD', str.lower) --> A B C A D
    return map(next, map(itemgetter(1), groupby(iterable, key)))

Personally, I would have written this with a generator expression rather than two map calls:

    return (next(group) for key, group in groupby(iterable, key))

… but the itertools docs are somewhat geared toward people who are thinking in Haskell or ML terms…


Also, let me add a quick plus for Erik Rose's more-itertools, which gives you all of the recipes from the itertools docs as code you can just import and run (after you pip install more-itertools, of course):

from more_itertools import unique_justseen as once_in_row
abarnert
  • 354,177
  • 51
  • 601
  • 671
2

Something like this:

def once_in_a_row(iterable):
     last_item = object()
     for item in iterable:
         if item != last_item:
             last_item = item
             yield item

Or if you want it to take a variable number of arguments:

def once_in_a_row(*args):
     last_item = object()
     for iterable in args:
         for item in iterable:
             if item != last_item:
                 last_item = item
                 yield item

See https://wiki.python.org/moin/Generators or Understanding Generators in Python

Community
  • 1
  • 1
JosiahDaniels
  • 2,411
  • 20
  • 37
  • 1
    This won't work correctly if the first variable in your iterator is None... Using `object()` for `last_value` as in @abarnet's answer avoids this – Claude Apr 27 '15 at 08:36
  • `Object` --> `object`. It's one of those nice python inconsitencies :( – Claude Apr 27 '15 at 08:40
  • 2
    @Claude: Yep, "all classes must have capitalized names… except a handful that were in the language before 2.2 when types and classes weren't the same thing". – abarnert Apr 27 '15 at 08:41
  • By the way, doing the `last_item = item` inside vs. outside the `if` makes a difference for types that aren't actually equivalence types. For example, if someone builds a float-wrapper type where `==` means "really close", my version will print the first new value that's different enough from the start of the group, yours won't. Which one is "better" is entirely a matter of what's appropriate for a given use case (and any use case for non-equivalent `==` is already pretty specialized), so it's worth having both versions… – abarnert Apr 27 '15 at 08:44
  • @JosiahDaniels What if the function has *args as paramater instead of iterable. And I want it to produces all the values in the first iterable, followed by all in the second, ... , all values in the last iterable. For example for i in once_in_a_row('abcd','ef','ghij'): produces the values 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', and 'j' – user3185653 Apr 27 '15 at 08:52
  • @user3185653 You do something like this: def once_in_a_row(*args): for iterable in args: ... – JosiahDaniels Apr 27 '15 at 08:54
  • @JosiahDaniels Yes for this defination I need not follow that print only if the next charcater is different. In this I need to print all characters., without any restriction. And also my code is giving error, please help : http://ideone.com/ftxmr0 – user3185653 Apr 27 '15 at 09:06
  • @user3185653 Your code isn't compiling because your whitespace is bad, there are a couple places that you have 5 spaces instead of 4. Also, ideone.com is using Python 2, so your `print` statements won't work. See http://stackoverflow.com/a/5598349/1106901 for how to do it in Python 2 vs 3. That should help with the errors. I really don't want to give you the answers to your homework though, so try reading tutorials :). Good luck! – JosiahDaniels Apr 27 '15 at 09:18
  • 1
    @user3185653: You've still got indentation errors. And they're still your fault, not JosiahDaniels' fault. Also, at this point it's becoming clear that you're not actually trying to understand the answers, you just want someone to hand you something that works so you can turn it in as your own work. – abarnert Apr 27 '15 at 09:38
1

Use itertools.groupby

from itertools import groupby
def once_in_a_row(iterable):
    for k, g in groupby(iterable):
        yield k

>>>generator = once_in_a_row("abbcccaadd")
>>>list(generator)
['a', 'b', 'c', 'a', 'd']
itzMEonTV
  • 19,851
  • 4
  • 39
  • 49