Home > Software engineering >  How to do iterate over a subset of a dictionary?
How to do iterate over a subset of a dictionary?

Time:05-02

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 and y/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)
  • Related