配列をユニークにしてその個数を返せ

ちょっと古い記事だけど、ζ*’ワ’)ζ<うっうー遅レス。より

配列をユニークにしてその個数とともに返せ。

具体的には、["foo", "bar", "foo", "baz", "bar", "foo"] を、[ ["foo", 3], ["bar", 2], ["baz", 1] ] にする。

面白いので勉強がてら考えてみます。例はRubyなので、配列を返しているけど、pythonなんでタプルで返すが良さそうですね。

戦略としては、まずリストをユニークな文字列の配列に変換し、ユニークな文字列ごとに数を数えるのが簡単そうです。
ユニークな文字の配列に変換するのは、setを使います。で、リスト内包を使って、タプルを生成してみます。

list=["foo", "bar", "foo", "baz", "bar", "foo"]
[(x, list.count(x)) for x in set(list)]

こんな結果になりました。

[('baz', 1), ('foo', 3), ('bar', 2)]

あれ?ちょっと結果が期待するものと違いますね。結果をソートしてないからですね。

list=["foo", "bar", "foo", "baz", "bar", "foo"]
sorted([(x, list.count(x)) for x in set(list)], cmp=lambda x,y:cmp(x[1],y[1]),reverse=True)

[('foo', 3), ('bar', 2), ('baz', 1)]

できた!
だけどちょっとコードが長いなあ。

listを何度もカウントしているところが、微妙にコストが高い気がしますが、まあオンメモリなんでたいしてコストはかからんでしょう。1行にまとめられると綺麗なんだけど、やりかたがよくわかりませんでした。まだまだ修行が足りないなあ。

ソートをリスト内包の直後に実行しようと、Noneが返ってくるのも謎。演算子の順番なのかしら?

[(x, list.count(x)) for x in set(list)].sort(cmp=lambda x,y:cmp(x[1],y[1]),reverse=True)

実行結果

None

なぜ?

pythonはリスト内包が強力ですね。最初は全然わからなかったけど、慣れてくるとリスト内包も楽しいな。