0

I'm I need to slice the leading character off the valued a dictionary - but only if the length of the value is greater than 1. Currently I'm doing this with a dictionary comprehension:

new_dict = {item[0]:item[1][1:] for item in old_dict if item.startswith('1')}

but I don't know how to modify this so that keys of length one are left alone.

The keys are the codewords of a Huffman code, and so start with '0' or '1'.

An example code is:

code = {'a':'0', 'b':'10', 'c':'110', 'd':'111'}

The above code works fine for 'b','c','d' but fails for 'a' (this is intensional - it's a unit test).

How do I correctly modify the above example to pass the test?

Tom Kealy
  • 2,537
  • 2
  • 27
  • 45

3 Answers3

1

I'm not sure which variable is where but you could do something along these lines.

new_dict = { item[0]:item[1][1] if len(item[1]) > 1 else item[0]:item[1] for item in old_dict if item.startswith('1') }
dutt
  • 7,909
  • 11
  • 52
  • 85
1

The nature of a comprehension is that it builds a new object iteratively, so you if you want every key in the original object old_dict to have a corresponding key in new_dict, you simply have to process every key.

Also, you say "I need to slice the leading character off the keys a dictionary", but the code you give slices the leading characters off the values. I assume you mean values. I suggest the following:

new_dict = {key:(value[:1] if len(value) > 1 else value) for key,value in old_dict.iteritems()}

Apart from using sequence assignment to make the iteration a bit clearer, I've used the if expression (equivalent to ternary operator in c-like languages) to incorporate the condition.

I've also dropped your original if clause, because I don't understand you to want to skip values starting with '1'.

Marcin
  • 48,559
  • 18
  • 128
  • 201
  • Sorry, I meant values - typing without thinking. – Tom Kealy Oct 14 '13 at 14:22
  • @TomKealy Also, did you want to omit values beginning with `'1'`? – Marcin Oct 14 '13 at 14:23
  • I'm sorting symbols into groups based on whether their codeword begins with a 1 or a zero. Then I need to do it again for the lists etc. I think that' an entirely different question though. – Tom Kealy Oct 14 '13 at 14:25
  • @TomKealy You might find it more efficient, but perhaps less clear to use one loop which does all of that. For one step, it's only going to halve the number of tests, so it may not matter. – Marcin Oct 14 '13 at 14:29
  • I'm working with small problem sizes at the minute. Should efficiency become an issue, I'll cross that bridge then. – Tom Kealy Oct 14 '13 at 14:30
  • Hi, sorry: I actually want to do something subtly different: only strip the leading char as I find those which begin with a 1, and returning a shorter dict without the symbols whose codeword begins with a 0 (i.e. {b:0, c:11, d:10} for the example above, but {e:'1'} for a hypothetical symbol e which has codeword '1'). I've only just realized I've asked the wrong question. – Tom Kealy Oct 14 '13 at 15:29
  • @TomKealy Well, this answer (together with your original) should allow you to do both in one comprehension. You'll just need to use both if clauses. – Marcin Oct 15 '13 at 14:43
0

If I understand your question correctly, you can accomplish it with this:

new_dict = {k:v[len(v)>1:] for k,v in old_dict.items()}

v[len(v)>1] will return the key if it is only 1 character, and it will strip off the leading character if it is more than one character

I'm not sure what you are trying to accomplish with if item.startswith('1') is a qualifier for your list comprehension but if you need it you can add it back on. May need to make it v.startswith('1') though.

Hoopdady
  • 2,296
  • 3
  • 25
  • 40