Sort strings accompanied by integers in list - python

I am trying to make a leaderboard.
Here is a list i have :
list=['rami4\n', 'kev13\n', 'demian6\n']
I would like to be able to sort this list from highest number to smallest, or even smallest to highest, giving something like :
list=['kev13\n', 'demian6\n', 'rami4\n']
I tried to use stuff like re.findall('\d+', list[loop])[0] but i only managed to get, out of the list, the best player. Not wanting to repeat the code for as many players as there are, does anyone have an idea ?

You indeed have to use the re module, but also the key parameter of the sort() method.
reg = re.compile('\w*?(\d+)\\n')
lst.sort(key=lambda s: int(reg.match(s).group(1)))
It works fine using findall() as you did too:
reg = re.compile('\d+')
lst.sort(key=lambda s: int(reg.findall(s)[0]))
Note that I compile() the regular expression so it is computed once and for all rather than for each element in the list.

I have an other solution based on Object Oriented Programming and the overriding of the __lt__ special methods of str.
import re
class SpecialString(str):
def __lt__(self, other):
pattern=re.compile(r"\d+")
return int(pattern.search(str(self)).group(0)) < int(pattern.search(str(other)).group(0))
if __name__ == "__main__":
listing = ['rami4\n', 'kev13\n', 'demian6\n']
spe_list = [SpecialString(x) for x in listing]
spe_list.sort()
print(spe_list)
Which print to the standard output:
['rami4\n', 'demian6\n', 'kev13\n']
This method allows you to not rewrite the sort function and use the built-in one (which is probably optimized). More over, since your strings may be thinked like "specialization of the str class", the inheritance mecanism is very suitable because you keep all its properties but re-write its comparison mecanism.

Related

Python list() function update to accept a non-iterable?

I've been working on a "learning project" which involves several short single-parameter functions, where the parameter might be either a numeric type or a list of numerics. E.g.,
def magnitude_of(v):
try:
return math.sqrt(sum([vi**2 for vi in v]))
except:
return abs(v)
This of course is necessary because if I did this:
def magnitude_of(v):
return math.sqrt(sum([vi**2 for vi in list(v)]))
...the code would fail because list() only accepts an iterable as an argument.
My question is: has there ever been consideration given at PSW to letting list() work with any argument? If so, what prevented the change from being implemented?
I would just create my own list function and use it for this particular purpose.
Ex:
def mlist(v):
try:
return list(v)
except(TypeError):
return [v]
Now you use mlist instead of list
l1 = mlist(1)
l2 = mlist([1])
Both will give [1] as the result
I cannot comment on whether this has come up at PSW, but I generally prefer using a ternary here instead of try... catch
e.g.
def magnitude_of(v):
return math.sqrt(sum([vi*vi for vi in (v if isinstance(v,list) else [v])]))
Granted, as others have mentioned it would probably be best if the function simply requires a list to be passed

How would you use the ord function to get ord("a") as 0?

For example, in python, when I type in ord("a") it returns 97 because it refers to the ascii list. I want ord("a") to return zero from a string that I created such as
alphabet = "abcdefghijklmnopqrstuvwxyz0123456789 .,?!"
so ord("b") would be 1 and ord("c") would be 2 ect.
How would I go about doing this?
You don't.
You're going about this the wrong way: you're making the mistake
This existing thing doesn't meet my needs. I want to make it meet my needs!
instead, the way to go about the problem is
This existing thing doesn't meet my needs. I need a thing that does meet my needs!
Once you realize that, the problem is now pretty straightforward. e.g.
DEFAULT_ALPHABET = "abcdefghijklmnopqrstuvwxyz0123456789 .,?!"
def myord(x, alphabet=DEFAULT_ALPHABET):
return alphabet.find(x)
Something like this should do the trick:
def my_ord(c):
alphabet = "abcdefghijklmnopqrstuvwxyz0123456789 .,?!"
return alphabet.index(c)
If i've understood correctly, this is what you want:
alphabet = "abcdefghijklmnopqrstuvwxyz0123456789 .,?!"
def crypt(c, key=97):
return ord(c)-key
def decrypt(c, key=97):
return chr(c+key)
dst = [crypt(c) for c in alphabet]
src = [decrypt(c) for c in dst]
print dst
print ''.join(src)
You can create a dict to map from characters to indices and then do lookups into that. This will avoid repeatedly searching the string as other answers are suggesting (which is O(n)) and instead give O(1) lookup time with respect to the alphabet:
my_ord_dict = {c : i for i, c in enumerate(alphabet)}
my_ord_dict['0'] # 26
At that point you can easily wrap it in a function:
def my_ord(c):
return my_ord_dict['0']
Or use the bound method directly
my_ord = my_ord_dict.__getitem__
But you don't want to change the name that refers to a builtin function, that'll confuse everyone else trying to use it that can see your change. If you are really trying to hurt yourself you can replace my_ord with ord in the above.

Check if a string is in List case-insentive [duplicate]

I love using the expression
if 'MICHAEL89' in USERNAMES:
...
where USERNAMES is a list.
Is there any way to match items with case insensitivity or do I need to use a custom method? Just wondering if there is a need to write extra code for this.
username = 'MICHAEL89'
if username.upper() in (name.upper() for name in USERNAMES):
...
Alternatively:
if username.upper() in map(str.upper, USERNAMES):
...
Or, yes, you can make a custom method.
str.casefold is recommended for case-insensitive string matching. #nmichaels's solution can trivially be adapted.
Use either:
if 'MICHAEL89'.casefold() in (name.casefold() for name in USERNAMES):
Or:
if 'MICHAEL89'.casefold() in map(str.casefold, USERNAMES):
As per the docs:
Casefolding is similar to lowercasing but more aggressive because it
is intended to remove all case distinctions in a string. For example,
the German lowercase letter 'ß' is equivalent to "ss". Since it is
already lowercase, lower() would do nothing to 'ß'; casefold()
converts it to "ss".
I would make a wrapper so you can be non-invasive. Minimally, for example...:
class CaseInsensitively(object):
def __init__(self, s):
self.__s = s.lower()
def __hash__(self):
return hash(self.__s)
def __eq__(self, other):
# ensure proper comparison between instances of this class
try:
other = other.__s
except (TypeError, AttributeError):
try:
other = other.lower()
except:
pass
return self.__s == other
Now, if CaseInsensitively('MICHAEL89') in whatever: should behave as required (whether the right-hand side is a list, dict, or set). (It may require more effort to achieve similar results for string inclusion, avoid warnings in some cases involving unicode, etc).
Usually (in oop at least) you shape your object to behave the way you want. name in USERNAMES is not case insensitive, so USERNAMES needs to change:
class NameList(object):
def __init__(self, names):
self.names = names
def __contains__(self, name): # implements `in`
return name.lower() in (n.lower() for n in self.names)
def add(self, name):
self.names.append(name)
# now this works
usernames = NameList(USERNAMES)
print someone in usernames
The great thing about this is that it opens the path for many improvements, without having to change any code outside the class. For example, you could change the self.names to a set for faster lookups, or compute the (n.lower() for n in self.names) only once and store it on the class and so on ...
Here's one way:
if string1.lower() in string2.lower():
...
For this to work, both string1 and string2 objects must be of type string.
I think you have to write some extra code. For example:
if 'MICHAEL89' in map(lambda name: name.upper(), USERNAMES):
...
In this case we are forming a new list with all entries in USERNAMES converted to upper case and then comparing against this new list.
Update
As #viraptor says, it is even better to use a generator instead of map. See #Nathon's answer.
You could do
matcher = re.compile('MICHAEL89', re.IGNORECASE)
filter(matcher.match, USERNAMES)
Update: played around a bit and am thinking you could get a better short-circuit type approach using
matcher = re.compile('MICHAEL89', re.IGNORECASE)
if any( ifilter( matcher.match, USERNAMES ) ):
#your code here
The ifilter function is from itertools, one of my favorite modules within Python. It's faster than a generator but only creates the next item of the list when called upon.
To have it in one line, this is what I did:
if any(([True if 'MICHAEL89' in username.upper() else False for username in USERNAMES])):
print('username exists in list')
I didn't test it time-wise though. I am not sure how fast/efficient it is.
Example from this tutorial:
list1 = ["Apple", "Lenovo", "HP", "Samsung", "ASUS"]
s = "lenovo"
s_lower = s.lower()
res = s_lower in (string.lower() for string in list1)
print(res)
My 5 (wrong) cents
'a' in "".join(['A']).lower()
UPDATE
Ouch, totally agree #jpp, I'll keep as an example of bad practice :(
I needed this for a dictionary instead of list, Jochen solution was the most elegant for that case so I modded it a bit:
class CaseInsensitiveDict(dict):
''' requests special dicts are case insensitive when using the in operator,
this implements a similar behaviour'''
def __contains__(self, name): # implements `in`
return name.casefold() in (n.casefold() for n in self.keys())
now you can convert a dictionary like so USERNAMESDICT = CaseInsensitiveDict(USERNAMESDICT) and use if 'MICHAEL89' in USERNAMESDICT:

remove multiple "classes" from a list in python

I have
class rel:
child=''
parent=''
listPar=[]
and in listPar I have a list of these classes (sorry for terms, I'm not sure if it is called class, is it?) so in listPar I have for example: room book ; book title ; room book;book title
And now im trying to remove all non unique occurences, so I want to have only
room book ; book title in listPar
Let's assume, that i have following code:
variable="Book"
variable2="Author"
toIns=rel()
toIns.parent=variable
toIns.child=variable2
listPar.append(toIns)
toIns2=rel()
toIns2.parent=variable
toIns2.child=variable2
listPar.append(toIns2)
and now how to remove all duplicates? (result ->
for elem in listPar:
print "child:",elem.child,"parent:",elem.parent
#child:author, parent:book
I have tried several things, but none of them seemed to fully work..could you please help me?
I'm presuming that the class you have given there isn't the actual class (as it would be worthless), but the easiest thing for you to do here - presuming the order of your elements doesn't matter to you, is to make your list into a set, which will remove all duplicates.
>>> a = ["test", "test", "something", "else"]
>>> a
['test', 'test', 'something', 'else']
>>> set(a)
{'test', 'something', 'else'}
Here I have use strings, but you could use any class that provides the equality operator and hash function. The equality function is used to check if the two classes are the same (as for a custom class, you need to define that) and a hash is used to make sets very efficient. Two classes giving the same hash must be the same. You can have two classes with the same hash that are not the same (it will fall back to the equality operator), but the more this happens the slower it will be. In general, using the sum of the hashes of the components of the class you use to check for equality is a good way to generate a decent hash.
So, for example:
class Book:
def __init__(self, title, author):
self.title = title
self.author = author
def __eq__(self, other):
return self.title == other.title and self.author == other.author
def __hash__(self):
return hash(self.title)+hash(self.author)
def __repr__(self):
return "Book("+repr(self.title)+", "+repr(self.author)+")"
We can use this class like before.
>>> a = [Book("Some Book", "Some Guy"), Book("Some Book", "Some Guy"), Book("Some Other Book", "Some Other Guy")]
>>> a
[Book('Some Book', 'Some Guy'), Book('Some Book', 'Some Guy'), Book('Some Other Book', 'Some Other Guy')]
>>> set(a)
{Book('Some Other Book', 'Some Other Guy'), Book('Some Book', 'Some Guy')}
If you do care about the order of the elements, even after removing duplicates, then you could do this:
def remove_duplicates_preserving_order(seq):
seen = set()
return [ x for x in seq if x not in seen and not seen.add(x)]
This works by hacking the dictionary comprehension a little - set.add() always returns 0, so you can check it is false (which it always will be) to add the element to the set.
Edit for update:
Please note that PEP-8 reccomends
using CapWords for classes, and lowercase_with_underscores for local
variables.
You seem to have a misunderstanding about how Python classes work. This class
doesn't make much sense, as these are all class attributes, not instance
attributes. This means that they will be the same for all instances of the
class, and that's not what you want. This means that when you change them the
second time, you will be changing it for all the instances, making them all
the same.
To make instance variables (the type you want) you want to create them inside
the constructor (__init__()) - check my example class to see how this works.
Once you have done this, you then need to implement __eq__() and __hash__()
functions so that Python knows what it means for two items of your class to be
equal. You can then use the methods I described above (either a set or the function
I gave) to remove duplicates.
Note that if this is all you wish to do with your data, a class might be overkill.
If you are always going to have two items, you could just use a tuple:
>>> a = [("Book", "Author"), ("Book", "Author"), ("OtherBook", "OtherAuthor")]
>>> set(a)
{('Book', 'Author'), ('OtherBook', 'OtherAuthor')}
As tuples already define equality for you as a sum of their parts.
Overall, you seem to lack an understanding of how classes are constructed and used in Python - I would suggest you go read up and learn how to use them before anything else, as not doing so will cause you a lot of problems.

python convert a string to an operator

Is it possible to convert a string to an operator in python?
I would like to pass a condition to a function
Ideally it would look like this:
def foo(self, attribute, operator_string, right_value):
left_value = getattr(self, attribute)
if left_value get_operator(operator_string) right_value:
return True
else:
return False
bar.x = 10
bar.foo('x', '>', 10)
[out] False
bar.foo('x', '>=', 10)
[out] True
I could make a dictionary where keys are strings and values are functions of the operator module.
I would have to change foo definition slightly:
operator_dict = {'>', operator.lt,
'>=', operator.le}
def foo(self, attribute, operator_string, right_value):
left_value = getattr(self, attribute)
operator_func = operator_dict[operator_string]
if operator_func(left_value, right_value):
return True
else:
return False
This means I have to make this dictionary, but is it really necessary?
You can use eval to dynamically build a piece of Python code and execute it, but apart from that there are no real alternatives. The dictionary-based solution is much more elegant and safe, however.
Apart from that, is it really that bad? Why not shorten it a bit …
return operator_dict[operator_string](left_value, right_value)
The way the problem is specified I don't see why you can't pass operator.le to the function instead of ">=".
If this operator_string coming from a database or file or something or are you passing it around in your code?
bar.foo('x', operator.le , 10)
Are you just looking to have a convenient shorthand? Then you might do something like:
from operator import le
bar.foo('x', le, 10)
If the real problem here is that you have code or business rules coming in from a database or datafile then maybe you actually need to look at writing a little parser that will map your input into these objects and then you could take a look at using a library like pyparsing, ply, codetalker, etc.
#This is very simple to do with eval()
score=1
trigger_conditon=">="
trigger_value=4
eval(f"{score}{trigger_conditon}{trigger_value}")
#luckily fstring also takes care of int/float or relavaent datatype
operator_str="ge"
import operator
eval(f"operator.{operator_str}({score},{trigger_value})")

Categories