I am very new to coding, but I have a real-life problem that I want to have some code for. I have a list of items, which each have locations - I put it into Python as something like objects = {"a": 0, "b": 7, "c": 24, "d": 2}
How do I loop through this dictionary and find the midpoints of each pair of items? I abbreviate midpoint as "x/y". So ultimately I want something that looks like, midpoints = {"a/b": 3.5, "a/c": 12, "a/d": 1, "b/c": 15.5, "b/d": 4.5, "c/d": 13}
.
I know you'd start with a function and a for loop, maybe something like
def mp(x,y):
return (x y)/2
midpoints = {}
for key1 in objects:
for key2 in objects:
midpoints[key1 "/" key2] = mp(objects[key1],objects[key2])
but this is giving useless midpoints ("a/a") as well as repeated midpoints (e.g. both "a/b" and "b/a").
I can't figure out how to clean this up...I guess you'd have to iterate over a subset of the dictionary? Any help would be greatly appreciated! :)
CodePudding user response:
You're looking for a cartesian product. Your implementation is almost correct, but the midpoints[key1 "/" key2] = z
line needs to be indented to run for every pair (key1, key2)
.
def mp(x,y):
return (x y)/2
midpoints = {}
for key1 in objects:
for key2 in objects:
z = mp(objects[key1],objects[key2])
midpoints[key1 "/" key2] = z
if you now print(midpoints)
, you'll obtain {'a/a': 0.0, 'a/b': 3.5, 'a/c': 12.0, 'a/d': 1.0, 'b/a': 3.5, 'b/b': 7.0, 'b/c': 15.5, 'b/d': 4.5, 'c/a': 12.0, 'c/b': 15.5, 'c/c': 24.0, 'c/d': 13.0, 'd/a': 1.0, 'd/b': 4.5, 'd/c': 13.0, 'd/d': 2.0}
. From your example I infer that you want to simplify this:
x/x
should not appear in the final dictionary;- only one of
x/y
andy/x
should appear in the dict
one way to achieve this would be to require key1 < key2
(lexical comparison):
for key1 in objects:
for key2 in objects:
if key1 < key2:
z = mp(objects[key1],objects[key2])
midpoints[key1 "/" key2] = z
this yields a dict similar to the one from your example. If you want to include x/x
, just change key1 < key2
to key1 <= key2
.
CodePudding user response:
You can use itertools.combinations
:
from itertools import combinations
objects = {"a": 0, "b": 7, "c": 24, "d": 2}
def mp(x, y):
return (x y) / 2
midpoints = {
f"{x}/{y}": mp(objects[x], objects[y]) for x, y in combinations(objects, 2)
}
print(midpoints)
Prints:
{"a/b": 3.5, "a/c": 12.0, "a/d": 1.0, "b/c": 15.5, "b/d": 4.5, "c/d": 13.0}
CodePudding user response:
To avoid duplicates, use enumerate
to obtain an index, and iterate through a sliced list of the objects in the inside loop:
midpoints = {}
items = list(objects.items()) # convert to a list so we can slice it
for i, (key_a, a) in enumerate(items[:-1]):
for key_b, b in items[i 1:]: # takes the remaining items (index > i)
midpoints[f"{key_a}/{key_b}"] = (a b) / 2
Alternatively use itertools.combinations
which generates all combinations of an iterable without repetition:
from itertools import combinations
midpoints = {}
for (key_a, a), (key_b, b) in combinations(objects.items(), 2):
midpoints[f"{key_a}/{key_b}"] = (a b) / 2
CodePudding user response:
You can perhaps try something like the below. The logic is to use a nested for loop for the two points, then use an if condition to only add in the value if numerator and denominator would be different points.
objects = {"a": 0, "b": 7, "c": 24, "d": 2}
midpoints = {}
for num in objects:
for denom in objects:
if num != denom:
midpoints[f'{num}/{denom}'] = (objects[num] objects[denom]) / 2
print(midpoints)