名前がリストのプレフィックスのいずれかで始まるかどうかを確認してから、次のように削除する必要があります。
_if name[:2] in ["i_", "c_", "m_", "l_", "d_", "t_", "e_", "b_"]:
name = name[2:]
_
上記は、長さが2のリスト接頭辞に対してのみ機能します。 可変長のプレフィックスにも同じ機能が必要です。
それはどのように効率的に行われますか(小さなコードと優れたパフォーマンス)?
Forループが各プレフィックスを反復処理し、name.startswith(prefix)
をチェックして、最後にプレフィックスの長さに従って名前をスライスしますが、これは多くのコードであり、おそらく非効率的で、「Python以外」です。
誰かが素晴らしい解決策を持っていますか?
少し読みにくいですが、これはうまくいきます:
name=name[len(filter(name.startswith,prefixes+[''])[0]):]
str.startswith(prefix [、start [、end]])¶
文字列がプレフィックスで始まる場合はTrueを返し、それ以外の場合はFalseを返します。 prefixは、検索する接頭辞のタプルにすることもできます。オプションの開始では、その位置で始まるテスト文字列。オプションの終了で、その位置で文字列の比較を停止します。
$ ipython
Python 3.5.2 (default, Nov 23 2017, 16:37:01)
Type 'copyright', 'credits' or 'license' for more information
IPython 6.4.0 -- An enhanced Interactive Python. Type '?' for help.
In [1]: prefixes = ("i_", "c_", "m_", "l_", "d_", "t_", "e_", "b_")
In [2]: 'test'.startswith(prefixes)
Out[2]: False
In [3]: 'i_'.startswith(prefixes)
Out[3]: True
In [4]: 'd_a'.startswith(prefixes)
Out[4]: True
for prefix in prefixes:
if name.startswith(prefix):
name=name[len(prefix):]
break
正規表現はおそらくあなたに最高の速度を与えます:
prefixes = ["i_", "c_", "m_", "l_", "d_", "t_", "e_", "b_", "also_longer_"]
re_prefixes = "|".join(re.escape(p) for p in prefixes)
m = re.match(re_prefixes, my_string)
if m:
my_string = my_string[m.end()-m.start():]
アンダースコアの前の文字になるようにプレフィックスを定義すると、次のことを確認できます
if name.partition("_")[0] in ["i", "c", "m", "l", "d", "t", "e", "b", "foo"] and name.partition("_")[1] == "_":
name = name.partition("_")[2]
正規表現、テスト済み:
import re
def make_multi_prefix_matcher(prefixes):
regex_text = "|".join(re.escape(p) for p in prefixes)
print repr(regex_text)
return re.compile(regex_text).match
pfxs = "x ya foobar foo a|b z.".split()
names = "xenon yadda yeti food foob foobarre foo a|b a b z.yx zebra".split()
matcher = make_multi_prefix_matcher(pfxs)
for name in names:
m = matcher(name)
if not m:
print repr(name), "no match"
continue
n = m.end()
print repr(name), n, repr(name[n:])
出力:
'x|ya|foobar|foo|a\\|b|z\\.'
'xenon' 1 'enon'
'yadda' 2 'dda'
'yeti' no match
'food' 3 'd'
'foob' 3 'b'
'foobarre' 6 're'
'foo' 3 ''
'a|b' 3 ''
'a' no match
'b' no match
'z.yx' 2 'yx'
'zebra' no match
filter
の使用についてはどうですか?
prefs = ["i_", "c_", "m_", "l_", "d_", "t_", "e_", "b_"]
name = list(filter(lambda item: not any(item.startswith(prefix) for prefix in prefs), name))
各リスト項目と接頭辞の比較は、最初の一致で効率的に停止することに注意してください。この動作は、any
値が見つかるとすぐに戻るTrue
関数によって保証されます。例:
def gen():
print("yielding False")
yield False
print("yielding True")
yield True
print("yielding False again")
yield False
>>> any(gen()) # last two lines of gen() are not performed
yielding False
yielding True
True
または、re.match
の代わりにstartswith
:
import re
patt = '|'.join(["i_", "c_", "m_", "l_", "d_", "t_", "e_", "b_"])
name = list(filter(lambda item: not re.match(patt, item), name))
検索と効率に関しては、常にアルゴリズムを改善するためのインデックス付け技術を考えます。プレフィックスの長いリストがある場合は、プレフィックスの最初の文字をdict
に単純にインデックス付けすることで、メモリ内インデックスを使用できます。
このソリューションは、プレフィックスの長いリストがあり、パフォーマンスが問題になる場合にのみ価値があります。
pref = ["i_", "c_", "m_", "l_", "d_", "t_", "e_", "b_"]
#indexing prefixes in a dict. Do this only once.
d = dict()
for x in pref:
if not x[0] in d:
d[x[0]] = list()
d[x[0]].append(x)
name = "c_abcdf"
#lookup in d to only check elements with the same first character.
result = filter(lambda x: name.startswith(x),\
[] if name[0] not in d else d[name[0]])
print result
これにより、オンザフライでリストが編集され、接頭辞が削除されます。 break
は、特定のアイテムに対してプレフィックスが見つかると、残りのプレフィックスをスキップします。
items = ['this', 'that', 'i_blah', 'joe_cool', 'what_this']
prefixes = ['i_', 'c_', 'a_', 'joe_', 'mark_']
for i,item in enumerate(items):
for p in prefixes:
if item.startswith(p):
items[i] = item[len(p):]
break
print items
['this', 'that', 'blah', 'cool', 'what_this']
単純な正規表現を使用できます。
import re
prefixes = ("i_", "c_", "longer_")
re.sub(r'^(%s)' % '|'.join(prefixes), '', name)
または、アンダースコアの前にあるものが有効なプレフィックスである場合:
name.split('_', 1)[-1]
これにより、最初のアンダースコアの前の任意の数の文字が削除されます。