简体   繁体   中英

How to iterate over the next n elements in a list from one point

So I have a list like this:

my_list = [{"id":21313,"remark":"","marks":"100"}, 
{"id":21314,"remark":"","marks":"29"},
{"id":21315,"remark":"","marks":"15"},
{"id":21316,"remark":"","marks":"50"},
{"id":21317,"remark":"","marks":"20"}]

The list have many elements. What I want to do is to iterate over the entire list,take a current element i as a point from which we check whether the marks of this point from the next two are more or less. If more than remark of good is made and if less than a remark of bad is made. This is what I want it to look like:

my_list = [{"id":21313,"remark":"good","marks":"100"}, 
{"id":21314,"remark":"bad","marks":"29"},
{"id":21315,"remark":"bad","marks":"15"},
{"id":21316,"remark":"NaN","marks":"50"},
{"id":21317,"remark":"NaN","marks":"20"}]

The last two are not available because there are not enough entries after them for comparison. Is there a way to do this?

You can make a slightly modified version of a sliding window iterator that also includes the last few elements:

from itertools import islice

def diminishing_window(seq, n=2):
    """
    (s0, ..., s[n-1]), (s1, ..., sn), ..., (s[-2], s[-1]), (s[-1])
    """
    it = iter(seq)
    result = tuple(islice(it, n))
    if len(result) == n:
        yield result
    for elem in it:
        result = result[1:] + (elem,)
        yield result
    result = result[1:]
    while result:
        yield result
        result = result[1:]

That will give you width n "windows" over the data until the last few, which will get progressively smaller. If we consider the first item in those windows to be the item we're "on", then we can compare it to the other items in the window to determine its result.

def dict_replace(d, **kwargs):
    res = d.copy()
    res.update(kwargs)
    return res

def get_remark(a, b):
    if len(b) < 2:
        return "NAN"
    elif all(int(a["marks"]) > int(d["marks"]) for d in b):
        return "good"
    else:
        return "bad"

new_list = [dict_replace(a, remark=get_remark(a, b)) for a, *b in diminishing_window(my_list, 3)]

print(new_list)
# [{'id': 21313, 'remark': 'good', 'marks': '100'}, {'id': 21314, 'remark': 'bad', 'marks': '29'}, 
#  {'id': 21315, 'remark': 'bad', 'marks': '15'}, {'id': 21316, 'remark': 'NAN', 'marks': '50'}, 
#  {'id': 21317, 'remark': 'NAN', 'marks': '20'}]

take a current element i as a point from which we check whether the marks of this point from the next two are more or less.

You can use zip_longest to iterate in groups of 3 using a for loop:

from itertools import zip_longest

# dictionary mapping for remark strings
rems = {1: 'good', 0: 'bad'}

for d1, d2, d3 in zip_longest(my_list, my_list[1:], my_list[2:], fillvalue={}):
    if not (d2 and d3):
        d1['remark'] = 'NaN'
    else:
        d1['remark'] = rems[int(d1['marks']) > max(int(d2['marks']), int(d3['marks']))]

print(my_list)

# [{'id': 21313, 'marks': '100', 'remark': 'good'},
#  {'id': 21314, 'marks': '29', 'remark': 'bad'},
#  {'id': 21315, 'marks': '15', 'remark': 'bad'},
#  {'id': 21316, 'marks': '50', 'remark': 'NaN'},
#  {'id': 21317, 'marks': '20', 'remark': 'NaN'}]

As an aside, you probably want to store these marks as integers (or floats) rather than strings. This will avoid having to call int each time for comparisons.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM