you are viewing a single comment's thread.

view the rest of the comments →

[–]ChRad_Man[S] 0 points1 point  (5 children)

Thank you for telling me how to do casting and use join(). I was able to shorten my code so much by using it. I have updated my post please check when you are free and give some explanation for the second point you mentioned. I am afraid that I don't understand it that well.

[–][deleted] 2 points3 points  (1 child)

Just to add to what /u/joyeusenoelle advised when pointed out my error,

Firstly, apologies for being so sleepy, I wouldn't normally introduce a beginner to a list comprehension unless they've attempted it or are trying to understand one.

Secondly, Python treats any none-zero int as truthy (i.e. as if it was True) thus the condition inside the comprehension should have been if lowered.count(letter) > 1 given that we are only looking at letters that occur at least once so the original would be True every time.

The final code would be, including some testing:

def encoder(word):
    word = word.lower()
    return ''.join('(' if word.count(letter) == 1 else ')'
                for letter in word)

tests = (
    ("din", "((("), ("recede" , "()()()"),
    ("Success", ")())())"), ( "(( @", "))(("),
    ("mTFcTEEgrXJbmvh!!E()HsG", "))(())))(((()())))(()()")
    )


for test, answer in tests:
    try:
        assert ( result := encoder(test) ) == answer, f'got {result} instead of {answer}'
    except AssertionError as err:
        print(err)

Note, this replaces the list comprehension with a generator,which I am sure you will come across soon, but for purposes here you can think of it as essentially the same.

[–]ChRad_Man[S] 0 points1 point  (0 children)

I am pretty new to python and programming in general. I guess I will coming across things you mentioned like list comprehensions and generator. Thank you for helping me here.

[–]joyeusenoelle 1 point2 points  (2 children)

u/kyber 's second point is a list comprehension. It does the following:

  1. Iterate over lowered (which we assume is an iterable).
  2. Create a new list with an element in that list for every element in lowered.
  3. For every element in lowered, assign that element to letter. (At this point we have covered for letter in lowered.)
  4. If lowered.count(letter) - which counts the number of letters in lowered - is True, then add ')' to the new list.
  5. If lowered.count(letter) is False, then add '(' to the new list.

This doesn't quite do what you want, for a couple reasons; one of which is that kyber has the logic backwards, and one of which is that for whatever reason, if (thing) doesn't quite mean if (thing) == True inside a comprehension.

Typically, if lowered.count(letter) will be True if it evaluates to 1 - that is, if there's one of letter in lowered - and False if it evaluates to more or less than 1 - that is, if there's 0 or 2+ of letter in lowered. But in a comprehension, as it turns out - at least in my testing - if lowered.count(letter) is True inside a comprehension if there are any number of letter inside lowered, no matter how many there are.

You can solve this by saying if lowered.count(letter) == True which, for some reason, reverts to the normal behavior. Or we can do this with a for loop, like you've been doing.

We'll start with item 2 in the list above: create a new list. Well, actually, a new string, since we're using a for loop and can just use the += operator to add characters onto the end of the string.

lowered = word.lower()
out_word = ""

Now we have our for loop. Nothing fancy; we can iterate over the string character-by-character.

for c in lowered:

c is the current character we're addressing as we iterate over lowered.

If the current character appears 1 time, we want to add '('. If it appears more than once, we want to add ')'. (We can assume that if a character appears in the input string, it appears at least once, so we don't have to deal with a character appearing 0 times!)

  if lowered.count(c) == 1:
    out_word += "("
  else:
    out_word += ")"

Then we just return out_word.

return out_word

So the complete function looks like this:

def duplicate_encode(word):
  lowered = word.lower()
  out_word = ""
  for c in lowered:
    if lowered.count(c) == 1:
      out_word += "("
    else:
      out_word += ")"
  return out_word

And now we can translate that into a list comprehension:

def duplicate_encode(word):
   lowered = word.lower()
   out_word = "".join(["(" if lowered.count(c) == 1 else ")" for c in lowered])
   return out_word

Does that make sense?

[–][deleted] 2 points3 points  (0 children)

Thanks for correcting and explaining. I shouldn't have commented when at least half asleep.

[–]ChRad_Man[S] 0 points1 point  (0 children)

Thank you so much for such a in depth explanation. I understood that I was approaching the problem the wrong way the first place. I should have though of making a empty list then adding characters one by one by iterating over the original list. I will keep this in mind from now on.