check for dictionary keys

M

micklee74

hi
in my code, i use dict(a) to make to "a" into a dictionary , "a" comes
from user input, so my program does not know in the first place. Then
say , it becomes

a = { '-A' : 'value1' , '-B' : "value2" , "-C" : "value3" , '-D' :
'value4' }

somewhere next in my code, i will check for these..:

1) -A and -B cannot exist together
2) -A and -C cannot exist together
3) -A and -B and -D cannot exist together
4) and lots of other combinations to check for....

how can i efficiently check for the above? At first as i do simple
checks , i use if and else.
But as i began to check for more combinatoiuns, it gets messy....

thanks.
 
F

faulkner

def all(s):
for x in s:
if not x: return False
return True


bad_combos = [['-A', '-B'], ['-A', '-C'], ...]
for bad_combo in bad_combos:
assert not all([bad_elem in a for bad_elem in bad_combo])
 
B

Bruno Desthuilliers

(e-mail address removed) a écrit :
hi
in my code, i use dict(a) to make to "a" into a dictionary , "a" comes
from user input, so my program does not know in the first place. Then
say , it becomes

a = { '-A' : 'value1' , '-B' : "value2" , "-C" : "value3" , '-D' :
'value4' }

somewhere next in my code, i will check for these..:

1) -A and -B cannot exist together
2) -A and -C cannot exist together
3) -A and -B and -D cannot exist together
4) and lots of other combinations to check for....

Looks like an option parser... If so, there's all you need in the
standard lib (look for the optparse module).
how can i efficiently check for the above? At first as i do simple
checks , i use if and else.
But as i began to check for more combinatoiuns, it gets messy....

First : use boolean logic (truth table, Kernaugh diagram, etc) to
simplify things. As an example, rule #3 is useless - it's a subset of
rule #1 (-A and -B and -D implies -A and -B). This should greatly reduce
the number of needed tests.

Then, write a simple rule system describing either valid inputs or
invalid inputs (preferably the smallest set !-). FWIW, it can be as
simple as a list of lambdas/error messages pairs, with lambdas being
predicate taking dict keys as params:


_RULES = [
(lambda keys : '-A' in keys and '-B' in keys,
"can't have both options -A and -B"),
(lambda keys : '-A' in keys and '-C' in keys,
"can't have both options -A and -C"),
# etc...
]

def validate(options, rules):
keys = options.keys()
for predicate, message in rules:
if not predicate(keys):
raise ValueError(message)
 
J

John Machin

(e-mail address removed) a écrit :

Looks like an option parser... If so, there's all you need in the
standard lib (look for the optparse module).


First : use boolean logic (truth table, Kernaugh diagram, etc) to
simplify things. As an example, rule #3 is useless - it's a subset of
rule #1 (-A and -B and -D implies -A and -B). This should greatly reduce
the number of needed tests.

Good idea, but doesn't scale well. Simple code can weed out redundant
rules, including any accidental duplicates that may creep into a long
list. See code listing at end.
Then, write a simple rule system describing either valid inputs or
invalid inputs (preferably the smallest set !-). FWIW, it can be as
simple as a list of lambdas/error messages pairs, with lambdas being
predicate taking dict keys as params:


_RULES = [
(lambda keys : '-A' in keys and '-B' in keys,
"can't have both options -A and -B"),
(lambda keys : '-A' in keys and '-C' in keys,
"can't have both options -A and -C"),
# etc...
]

The evil HR director won't let the PHB pay me on a per LOC basis, so
I've had to come up with a compact table-driven approach :)
def validate(options, rules):
keys = options.keys()
for predicate, message in rules:
if not predicate(keys):
raise ValueError(message)

Cheers,
John

C:\junk>type option_combos.py
bad_combos = ['ABD', 'AC', 'AB', 'CA']

def rule_compaction(bc_list, verbose=False):
# The next few lines are admittedly oldfashioned :)
bc_sets = [set(x) for x in bc_list]
deco = [(len(y), y) for y in bc_sets]
deco.sort()
bc_sets = [z[1] for z in deco]
del deco
if verbose:
print "bc_sets #1:", bc_sets
for k in xrange(len(bc_sets)-1, 0, -1):
candidate = bc_sets[k]
for ko in bc_sets[:k]:
if ko <= candidate:
if verbose:
print candidate, "knocked out by", ko
del bc_sets[k]
break
if verbose:
print "bc_sets #2:", bc_sets
return bc_sets

option_rules = rule_compaction(bad_combos, verbose=True)

def combo_disallowed_by(opt_set, rules):
for rule in rules:
if opt_set >= rule:
return rule
return None # redundantly, for emphasis

if __name__ == "__main__":
import sys
for opt_string in sys.argv[1:]:
failer = combo_disallowed_by(set(opt_string), option_rules)
if failer:
print repr(opt_string), "disallowed by", failer
else:
print repr(opt_string), "is OK"

=== a test ===

C:\junk>option_combos.py A AB AC AD BC ABD ABX XBA BX
bc_sets #1: [set(['A', 'C']), set(['A', 'B']), set(['A', 'C']),
set(['A', 'B', 'D'])]
set(['A', 'B', 'D']) knocked out by set(['A', 'B'])
set(['A', 'C']) knocked out by set(['A', 'C'])
bc_sets #2: [set(['A', 'C']), set(['A', 'B'])]
'A' is OK
'AB' disallowed by set(['A', 'B'])
'AC' disallowed by set(['A', 'C'])
'AD' is OK
'BC' is OK
'ABD' disallowed by set(['A', 'B'])
'ABX' disallowed by set(['A', 'B'])
'XBA' disallowed by set(['A', 'B'])
'BX' is OK

=== the end ===
 
B

bruno at modulix

John said:
Good idea, but doesn't scale well.

Does it need to scale ? If there are lot of rules and frequently
changing, yes, automating the process will be a good idea - but if it's
about a program options, just using one's brain might be enough. At
least it forces one to think about what's going on...
Simple code can weed out redundant
rules,

Simple code can weed out redundant *simple* rules !-)

(snip)
Then, write a simple rule system describing either valid inputs or
invalid inputs (preferably the smallest set !-). FWIW, it can be as
simple as a list of lambdas/error messages pairs, with lambdas being
predicate taking dict keys as params:


_RULES = [
(lambda keys : '-A' in keys and '-B' in keys,
"can't have both options -A and -B"),
(lambda keys : '-A' in keys and '-C' in keys,
"can't have both options -A and -C"),
# etc...
]

The evil HR director won't let the PHB pay me on a per LOC basis, so
I've had to come up with a compact table-driven approach :)

<ot>I'm my own evil HR director and PHB !-)</ot>

Don't like table-driven programming, John ?

This solution takes very few lines, and while it's surely not a
full-blown rule engine, it's at least reasonably flexible.

(Not to say it's better than yours - it's of course a matter of
effective use case).
def validate(options, rules):
keys = options.keys()
for predicate, message in rules:
if not predicate(keys):
raise ValueError(message)



C:\junk>type option_combos.py
bad_combos = ['ABD', 'AC', 'AB', 'CA']

def rule_compaction(bc_list, verbose=False):
# The next few lines are admittedly oldfashioned :)
bc_sets = [set(x) for x in bc_list]
deco = [(len(y), y) for y in bc_sets]
deco.sort()
bc_sets = [z[1] for z in deco]
del deco
if verbose:
print "bc_sets #1:", bc_sets
for k in xrange(len(bc_sets)-1, 0, -1):
candidate = bc_sets[k]
for ko in bc_sets[:k]:
if ko <= candidate:
if verbose:
print candidate, "knocked out by", ko
del bc_sets[k]
break
if verbose:
print "bc_sets #2:", bc_sets
return bc_sets

Nice code - but how does it handle more complex predicates ? Seems you
can only deal with 'and' rules here. <nitpick>"Doesn't scale well", you
said ?-)</nitpick>

(snip)
 
J

John Machin

John said:
Good idea, but doesn't scale well.

Does it need to scale ? If there are lot of rules and frequently
changing, yes, automating the process will be a good idea - but if it's
about a program options, just using one's brain might be enough. At
least it forces one to think about what's going on...
Simple code can weed out redundant
rules,

Simple code can weed out redundant *simple* rules !-)

(snip)
Then, write a simple rule system describing either valid inputs or
invalid inputs (preferably the smallest set !-). FWIW, it can be as
simple as a list of lambdas/error messages pairs, with lambdas being
predicate taking dict keys as params:


_RULES = [
(lambda keys : '-A' in keys and '-B' in keys,
"can't have both options -A and -B"),
(lambda keys : '-A' in keys and '-C' in keys,
"can't have both options -A and -C"),
# etc...
]
The evil HR director won't let the PHB pay me on a per LOC basis, so
I've had to come up with a compact table-driven approach :)

<ot>I'm my own evil HR director and PHB !-)</ot>

Don't like table-driven programming, John ?

I love table-driven programming. You seem to misunderstand; In this case
the "bad combos" list *is* the table.
This solution takes very few lines, and while it's surely not a
full-blown rule engine, it's at least reasonably flexible.

(Not to say it's better than yours - it's of course a matter of
effective use case).
[snip]

Nice code - but how does it handle more complex predicates ? Seems you
can only deal with 'and' rules here.

More complex predicates are not in the OP's spec ... this is a defence I
learned from somebody in this newsgroup recently :)
<nitpick>"Doesn't scale well", you
said ?-)</nitpick>

I understand 'scale' to relate to size, not to complexity.

Cheers,
John
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,756
Messages
2,569,540
Members
45,025
Latest member
KetoRushACVFitness

Latest Threads

Top