I am making a bot which checks if a message contains a key with a value of "weak", if it does, it should send the keys that are in the message.
However, my code faces two issues.
when i do !grading aban, it gives me a response, however, if i do !grading abi ayyash, (which has spaces) it no longer works,
and also, my code should check if the SPECIFIC key is in the message, if I had a key of "aban test", but I also had a key called "aban" and "test", it shouldnt show me "aban" and "test" seperately as found, but only "aban test"
here is the code.
@commands.command()
async def grading(self, ctx, *,message):
found_keys = [weak for weak in message if weak in narrators.keys()]
nars = ', '.join(found_keys)
await ctx.send("This is unreliable because of:\n"
nars)
heres the json
{"aban": "weak", "aban b. abi ayyash": "weak", "abi ayyash": "weak"}
CodePudding user response:
This is caused by your loop and the *
in your argument list.
async def command(ctx, *, message):
In this (your) case, !command a string with spaces
will result in message
being "a string with spaces"
, which is what you want (judging from your question).
However, as you're looping over the message ([weak for weak in message]
), you're looping over every single character of the string, so ("a", " ", "s", "t", ...)
and none of those are in your dictionary.
I believe you want to check it the other way around: check if the keys from the dictionary are in the message.
Another option is to check separate words in the message (instead of characters) & loop over that, by changing your parameters to *message
instead of *, message
(note the place where the *
is). This will split your message into a tuple of separate words.
This way, however, you can no longer check for keys of multiple words. That means you'll have to make your loop a bit more complicated. Keep track of a string of previously-seen keys. In every iteration see if {previous} {current}
is in your dict. If it is, keep going (potentially a longer one of more words is present). If it isn't, then the previous one is the longest possible match & you should add it to the list of "weak words".
This slightly more complicated (it's like 4 lines of basic Python, it's not that hard) loop is to account for your second issue, where you wanted the longest possible key.
CodePudding user response:
What you are looking for are combinations, and there is a function in itertools called combinations
that can be used.
We also need to use all the lengths, from 1 to the len(message) 1
, so that we exhaust all possibilities. We use the unpacking operator *
so that we can chain it afterwards.
Also you don't need to use the .keys() method if you are using the in
operator, as it will automatically check the keys.
from itertools import combinations, chain
@commands.command()
async def grading(self, ctx, *message):
found_keys = [weak for weak in chain(message, *(combinations(message, i) for i in range(1, len(message) 1))) if (weak_key := " ".join(weak)) in narrators]
nars = ', '.join(found_keys)
await ctx.send("This is unreliable because of:\n" nars)
Just for context, this is equivalent to below (just in case it wasn't clear).
from itertools import combinations, chain
@commands.command()
async def grading(self, ctx, *message):
found_keys = []
for key_group in chain(message, *(combinations(message, i) for i in range(1, len(message) 1))):
weak = " ".join(key_group)
if weak in narrators:
found_keys.append(weak)
nars = ', '.join(found_keys)
await ctx.send("This is unreliable because of:\n" nars)
if you would like to have unordered pairing as well, use permutations
rather than combinations
. Look at the examples on the documentation page for more context