How does this lambda function work exactly? - python

Can anybody explain to me please what exactly does this lambda function do?
from collections import Counter
def solve(a):
c = Counter(a)
return sorted(a, key=lambda k: (-c[k], k))
Thanks beforehand!

A lambda function is just like any other function, it's just expressed in a more compact way - so breaking it down:
lambda k : ( -c[k], k )
Is equivalent to:
def lambdafunction(k):
return (-c[k], k )
Where c is some in-scope variable - which per your solve function is a Counter.
The contents of that counter are keys and variables, and the lambda extracts those values and multiplies them by minus one, it then builds a tuple containing this extracted, negated value as the first entry, and the key as the second entry. These tuples are then used to perform the sort on the object to be solved, sorting the elements by frequency - most frequent first, with tie-breaking (i.e. where two or more elements share the same frequency) performed on the natural object.
e.g.
alist = ["a", "a", "b", "b", "b", "c", "c", "c", "c", "c", "c", "d", "d"]
solve(alist)
>> ['c', 'c', 'c', 'c', 'c', 'c', 'b', 'b', 'b', 'a', 'a', 'd', 'd']
Internally, there's a Counter which contains the values:
Counter({'a': 2, 'b': 3, 'c': 6, 'd': 2})
The lambda function converts these to tuples, which it associates with each element of the list before sorting them:
( -6, "c" )
( -3, "b" )
( -2, "a" )
( -2, "d" )
So all the "c" items appear at the top of the list, because the internally calculated tuples associated with them ( -6, "c" ) come first.
Using a lambda function like this within the sorted function gives sorted the flexibility to sort using whatever method you like - you define the function used to describe exactly what aspects of the collection you want sorted.

Counter(a) counts how many times each element is present in a, so this sorts a from most often element to least often element and when counts are the same it sorts alphabetically

Related

finding values in dictionary based on their key

I'm trying to find the values of keys based on their 3 first letters. I have three different categories of subjects that i have to get the grade from, stored as value with the subject being key. I have ECO, GEO, and INF. As there are multiple subjects i want to get the values from every key containing either ECO, GEO or INF.
subject={"INFO100":"A"}
(subject.get("INF"))
In this method i don't get the value, i have to use the whole Key. Is there a work-a-round? I want the values seperately so i can calculate their GPA based on their field of study:)
You need to iterate on the pairs, to filter on the key and keep the value
subject = {"INFO100": "A", "INF0200": "B", "ECO1": "C"}
grades_inf = [v for k, v in subject.items() if k.startswith("INF")]
print(grades_inf) # ['A', 'B']
grades_eco = [v for k, v in subject.items() if k.startswith("ECO")]
print(grades_eco) # ['C']
A said in the comments, the purpose of a dictionary is to have unique keys. Indexing is extremely fast as it uses hash tables. By searching for parts of the keys you need to loop and lose the benefit of hashing.
Why don't you store your data in a nested dictionary?
subject={'INF': {"INFO100":"A", "INFO200":"B"},
'OTH': {"OTHER100":"C", "OTHER200":"D"},
}
Then access:
# all subitems
subject['INF']
# given item
subject['INF']['INFO100']
For understanding porpoises, you can create a function that returns a dictionary, like:
def getGradesBySubject(dict, search_subject):
return [grade for subject,grade in dict.iteritems() if subject.startwith(search_subject)]
I'd suggest using a master dict object that contains a mapping of the three-letter subjects like ECO, GEO, to all subject values. For example:
subject = {"INFO100": "A",
"INFO200": "B",
"GEO100": "D",
"ECO101": "B",
"GEO003": "C",
"INFO101": "C"}
master_dict = {}
for k, v in subject.items():
master_dict.setdefault(k[:3], []).append(v)
print(master_dict)
# now you can access it like: master_dict['INF']
Output:
{'INF': ['A', 'B', 'C'], 'GEO': ['D', 'C'], 'ECO': ['B']}
If you want to eliminate duplicate grades for a subject, or just as an alternate approach, I'd also suggest a defaultdict:
from collections import defaultdict
subject = {"INFO100": "A",
"INFO300": "A",
"INFO200": "B",
"GEO100": "D",
"ECO101": "B",
"GEO003": "C",
"GEO102": "D",
"INFO101": "C"}
master_dict = defaultdict(set)
for k, v in subject.items():
master_dict[k[:3]].add(v)
print(master_dict)
defaultdict(<class 'set'>, {'INF': {'B', 'A', 'C'}, 'GEO': {'D', 'C'}, 'ECO': {'B'}})

How to create X lists from X files that are in a list and assign X values at once to a dict keys from the created lists. [python]

So i try to make X number of lists where X is the number of nargs provided in my program.
Example: python program.py --payloads file1 file2 file3 file4... up to 20 nargs.
This gives me a list of: payloads_sets = [file1, file2, file3, file4] (takes up to 20 files)
Now, here is the tricky part where i got stuck. I need to read from each file and iterate through each payload in the file simultaneously to set it in the request I'll make for every payload in the files.
TL;DR: There is a different payload set for each defined position (up to a
maximum of 20). The attack iterates through all payload sets simultaneously and places one payload into each defined position.
For example:
Payload_set_1 = [1, 2, 3,...]
Payload_set_2 = [a, b, c,...]
Payload_set_3 = [x, y, z,...]
data = {var_a: marked_position_1,
var_b: marked_position_2,
var_c: marked_position_3}
The desired sent requests:
for loop of something: send this:
data = {var_a: 1, var_b: 'a', var_c: 'x'}
data = {var_a: 2, var_b: 'b', var_c: 'y'}
data = {var_a: 3, var_b: 'c', var_c: 'z'}
Hope someone can clarify that for me or suggest another method to make it work.
Long story short, I need to replicate what BurpSuite's Intruder Pitchfork attack does.
You can use the zip function with destructuring in order to do this:
var_names = ["var_a", "var_b", "var_c"]
values = [
[1, 2, 3],
["a", "b", "c"],
["x", "y", "z"]
]
# iterate through the values together
for cur_values in zip(*values):
# match the values with their name
named_values = zip(var_names, cur_values)
# use the iterator of tuple constructor to make a dict
value_dict = dict(named_values)
print(value_dict)
Output:
{'var_a': 1, 'var_b': 'a', 'var_c': 'x'}
{'var_a': 2, 'var_b': 'b', 'var_c': 'y'}
{'var_a': 3, 'var_b': 'c', 'var_c': 'z'}

How to convert the if else logic into dynamic selection?

I am writing logic to compare a few values.
I have three lists of values and one rule list
new_values = [1,0,0,0,1,1]
old_1 = [1,1,1,0,0,1]
old_2 = [1,0,1,0,1,1]
# when a is correct #when b is correct # if both are correct # if both are wrong
rules = ['a', 'b', 'combine', 'leave']
What I am looking for is, compare new_values to old_1 and old_2 values based on that select rule from rules list.
something like this:
def logic(new_values, old_values, rules):
rules_result = []
for new_value, old_value_1, old_value_2 in zip(new_values, old_values[0], old_values[1]):
if new_value == old_value_1 and new_value == old_value_2:
# if both are correct
rules_result.append(rules[2])
elif new_value == old_value_1:
# if a is correct
rules_result.append(rules[0])
elif new_value == old_value_2:
# if b is correct
rules_result.append(rules[1])
elif new_value!= old_value_1 and new_value!= old_value_2:
# if both are wrong
rules_result.append(rules[3])
return rules_result
Running this code with one rule list gives me this result :
logic(new_values, [old_1, old_2], rules)
output
['combine', 'b', 'leave', 'combine', 'b', 'combine']
I am facing issue to make this code dynamic if I have to compare more than two old values list, let say If I have three lists of old values and then my rule list will expand for each combination
new_values = [1,0,0,0,1,1]
old_1 = [1,1,1,0,0,1]
old_2 = [1,0,1,0,1,1]
old_3 = [0,0,0,1,1,1]
# when a is correct #when b is correct # if a and b are correct # if a and c are correct #if b and c are correct' #if all three are correct # if all three are wrong
rules = ['a', 'b', 'combine a_b', 'select c', 'combine b_c', 'select a', 'combine']
I am getting rules and values from a different function, I am looking for a rule selection function, where pass the list of old values ( example 2,3,4 list ) with new value and rule list, then dynamically compare each old list with new value list and select the rule from rule list.
How to make logic function dynamic to work on more than two old list values?
This problem could be solved easily if you use the concept of truth table. Your rules list defines the outcome for some boolean values. It doesn't consist of 1's and 0's so it can't be expressed by truth functions like and, or, xor but it's not a problem. You can simply rearrange your list by considering the order in the truth table:
# for 2 boolean variables, there are 2 ^ 2 = 4 possibilities
# ab ab ab ab
# 00 01 10 11
rules = ["leave", "b", "a", "combine"]
You can also turn this into a dict so you don't need to comment them to remember which one is what (and as a bonus, it will look like a truth table :)):
# ab
rules = {"00": "leave",
"01": "b",
"10": "a",
"11": "combine"}
Now, define a function to get the related key value for your boolean variables:
def get_rule_key(reference, values):
""" compares all the values against reference and returns a string for the result"""
return "".join(str(int(value == reference)) for value in values)
And your logic function will be simply this:
def logic(new_values, old_values, rules):
rules_result = []
for new_value, *old_values in zip(new_values, *old_values):
key = get_rule_key(new_value, old_values)
rules_result.append(rules.get(key))
return rules_result
print(logic(new_values, [old_1, old_2], rules))
# ['combine', 'b', 'leave', 'combine', 'b', 'combine']
For triples update your rules accordingly:
# for 3 boolean variables, there are 2 ^ 3 = 8 possibilities
# abc
rules = { "000": "combine",
# "001": Not defined in your rules,
"010": "b",
"011": "combine b_c",
"100": "a",
"101": "select c",
"110": "combine a_b"}
"111": "select a"}
print(logic(new_values, [old_1, old_2, old_3], rules))
# ['combine a_b', 'combine b_c', None, 'combine a_b', 'combine b_c', 'select a']
Notes:
None appears in the output because your rules doesn't define what is the output for "001" and dict.get returns None by default.
If you want to use a list to define the rules you have to define all the rules in order and convert the result of get_rule_key to an integer: "011" -> 3. You can manage this with int(x, base=2).
With unknown inputs it will be difficult to get this labels you specify.
It would be easy to map which ouf the old values corresponds to the same new value (positionally speaking). You can use a generic test function that gets a "new" value and all "old" values on that position, map the old values to 'a'... and return which ones correlate:
new_values = [1,0,0,0,1,1]
old_1 = [1,1,1,0,0,1]
old_2 = [1,0,1,0,1,1]
old_3 = [0,0,0,1,1,1]
old_4 = [0,0,0,1,1,1]
old_5 = [0,0,0,1,1,1]
def test(args):
nv, remain = args[0], list(args[1:])
start = ord("a")
# create dict from letter to its corresponding value
rv = {chr(start + i):v for i,v in enumerate(remain)}
# return tuples of the input and the matching outputs
return ((nv,remain), [k for k,v in rv.items() if v == nv])
rv = []
for values in zip(new_values, old_1, old_2, old_3, old_4, old_5):
rv.append(test(values))
print(*rv,sep="\n")
print([b for a,b in rv])
Output (manually spaced out):
# nv old_1 old_2 old_3 old_4 old_5
# a b c d e
((1, [ 1, 1, 0, 0, 0]), ['a', 'b'])
((0, [ 1, 0, 0, 0, 0]), ['b', 'c', 'd', 'e'])
((0, [ 1, 1, 0, 0, 0]), ['c', 'd', 'e'])
((0, [ 0, 0, 1, 1, 1]), ['a', 'b'])
((1, [ 0, 1, 1, 1, 1]), ['b', 'c', 'd', 'e'])
((1, [ 1, 1, 1, 1, 1]), ['a', 'b', 'c', 'd', 'e'])
[['a', 'b'], ['b', 'c', 'd', 'e'], ['c', 'd', 'e'], ['a', 'b'],
['b', 'c', 'd', 'e'], ['a', 'b', 'c', 'd', 'e']]
You could then map the joined results to some output:
# mapping needs completion - you'll need to hardcode that
# but if you consider inputs up to 5 old values, you need to specify those
# combinations that can happen for 5 as 4,3,2 are automagically included
# iif you omit "all of them" as result.
mapper = {"a": "only a", "ab": "a and b", "ac":"a and c", "abcde": "all of them",
# complete to your statisfaction on your own}
for inp, outp in rv:
result = ''.join(outp)
print(mapper.get(result , f"->'{result}' not mapped!"))
to get an output of:
a and b
->'bcde' not mapped!
->'cde' not mapped!
a and b
->'bcde' not mapped!
all of them

Reformatting a dict where the values have a dict-like relationship

I have a defaultdict that looks like this:
d = { 'ID_001': ['A', 'A_part1', 'A_part2'],
'ID_002': ['A', 'A_part3'],
'ID_003': ['B', 'B_part1', 'B_part2', 'A', 'A_part4'],
'ID_004': ['C', 'C_part1', 'A', 'A_part5', 'B', 'B_part3']
}
Before I go any further, I have to say that A_part1 isn't the actual string -- the strings are really a bunch of alphanumeric characters; I represented it as such to show that A_part1 is text that is associated with A, if you see what I mean.)
Standing back and looking at it, what I really have is a dict where the values have their own key/value relationship, but that relationship exists only in the order they appear in, in the list.
I am attempting to end up with something like this:
['ID_001 A A_part1, A_part2',
'ID_002 A A_part3',
'ID_003 B B_part1 B_part2',
'ID_003 A A_part4',
'ID_004 C C_part1',
'ID_004 A A_part5',
'ID_004 B B_part3']
I have made a variety of attempts; I keep wanting to run through the dict's value, making note of the character in the first position (eg, the A), and collect values until I find a B or a C, then stop collecting. Then append what I have to a list that I have declared elsewhere. Ad nauseum.
I'm running into all sorts of problems, not the least of which is bloated code. I'm missing the ability to iterate through the value in a clean way. Invariably, I seem to run into index errors.
If anyone has any ideas/philosophy/comments I'd be grateful.
What about something like:
d = { 'ID_001': ['A', 'A_part1', 'A_part2'],
'ID_002': ['A', 'A_part3'],
'ID_003': ['B', 'B_part1', 'B_part2', 'A', 'A_part4'],
'ID_004': ['C', 'C_part1', 'A', 'A_part5', 'B', 'B_part3']
}
def is_key(s):
return s in ['A','B','C']
out = {}
for (k,v) in d.iteritems():
key = None
for e in v:
if is_key(e): key = e
else:
out_key = (k,key)
out[out_key] = out.get(out_key, []) + [e]
which generates:
{('ID_001', 'A'): ['A_part1', 'A_part2'],
('ID_002', 'A'): ['A_part3'],
('ID_003', 'A'): ['A_part4'],
('ID_003', 'B'): ['B_part1', 'B_part2'],
('ID_004', 'A'): ['A_part5'],
('ID_004', 'B'): ['B_part3'],
('ID_004', 'C'): ['C_part1']}
It's important that you update the is_key function to match your actual input.
Also, the variable names are far from optimal, but I'm not really sure what you're doing -- you should be able to (and should) give them more appropriate names.
May not be in the order you want, but no thanks for further headaches.
d = { 'ID_001': ['A', 'A_part1', 'A_part2'],
'ID_002': ['A', 'A_part3'],
'ID_003': ['B', 'B_part1', 'B_part2', 'A', 'A_part4'],
'ID_004': ['C', 'C_part1', 'A', 'A_part5', 'B', 'B_part3']
}
rst = []
for o in d:
t_d={}
for t_o in d[o]:
if not t_o[0] in t_d:
t_d[t_o[0]] = [t_o]
else: t_d[t_o[0]].append(t_o)
for t_o in t_d:
rst.append(' '.join([o,t_d[t_o][0],', '.join(t_d[t_o][1:])]))
print(rst)
https://ideone.com/FeBDLA
['ID_004 C C_part1', 'ID_004 A A_part5', 'ID_004 B B_part3', 'ID_003 A A_part4', 'ID_003 B B_part1, B_part2', 'ID_002 A A_part3', 'ID_001 A A_part1, A_part2']
Whenever you're trying to do something involving contiguous groups, you should think of itertools.groupby. You weren't very specific about what condition separates the groups, but if we take "the character in the first position" at face value:
from itertools import groupby
new_list = []
for key, sublist in sorted(d.items()):
for _, group in groupby(sublist, key=lambda x: x[0]):
new_list.append(' '.join([key] + list(group)))
produces
>>> for elem in new_list:
... print(elem)
...
ID_001 A A_part1 A_part2
ID_002 A A_part3
ID_003 B B_part1 B_part2
ID_003 A A_part4
ID_004 C C_part1
ID_004 A A_part5
ID_004 B B_part3

List Comprehension in Nested Lists

I have a list like [["foo", ["a", "b", "c"]], ["bar", ["a", "b", "f"]]]
and I'm wanting to split it out so I can get a count of the total number of As, Bs, etc. but I'm new to Python and having a bit of a time of it.
I'm using [lx for lx in [li[1] for li in fieldlist if li[1]]] to try and get a list with all of the items in the sub-sublists, but that returns a list with the first sublists ([["a", "b", "c"], ["a", "b", "f"]] instead of a list with the contents of those sublists. I'm pretty sure I'm just thinking about this wrong, since I'm new to list comprehensions and Python.
Anyone have a good way to do this? (and yes, I know the names I chose (lx, li) are horrible)
Thanks.
This will give you the list you want:
[lx for li in fieldlist for lx in li[1] if li[1]]
A Pythonic solution would be something like:
>>> from collections import Counter
>>> Counter(v for (field, values) in fieldlist
... for v in values)
Counter({'a': 2, 'b': 2, 'c': 1, 'f': 1})
List comprehension:
>>> s = [["foo", ["a", "b", "c"]], ["bar", ["a", "b", "f"]]]
>>> [x for y, z in s for x in z]
['a', 'b', 'c', 'a', 'b', 'f']
>>>
What is the purpose of your if li[1]? If li[1] is an empty list or other container, the test is redundant. Otherwise you should edit your question to explain what else it could be.

Categories

Resources