-1

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?

jpp
  • 159,742
  • 34
  • 281
  • 339
Faisal Afzal
  • 255
  • 1
  • 11

2 Answers2

1

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'}]
Patrick Haugh
  • 59,226
  • 13
  • 88
  • 96
1

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.

jpp
  • 159,742
  • 34
  • 281
  • 339