Round a number to nearest in a given range

Good morning,
I found a solution to round a number to the nearest in a range of values, is there a custom node and/or a more efficient DesignScript or Python solution


Forum english 24 juillet 2023.dyn (31.7 KB)

Thanking you in advance
cordially
christian.stan

1 Like

Design Script:

sorted = List.Sort(List.AddItemToEnd(values@L1<1>, range));
indx = List.IndexOf(sorted@L2<1>, values@L1<1>);
indexes = List.Transpose([indx - 1, indx, indx + 1]);
set = List.GetItemAtIndex(sorted@L2<1>, indexes@L2<1>);
val = List.GetItemAtIndex(set@L2<1>, 1);
prior = List.GetItemAtIndex(set@L2<1>, 0);
next = List.GetItemAtIndex(set@L2<1>, 2);
mappedValues = (((val - prior) < (next - val) ? prior : next));

Works by adding the item into the list, and getting the value proceeding and following it, and finding which has less offset from the two. You do get an error if you provide a value outside of the limits of the range though, but that is good in some cases as it’d be outside your design limits.

2 Likes

Thanks mate, brilliant as usual
I enclose a python solution realized with great difficulty

import sys
import math
lst_values=IN[0]
lst_r=IN[1]
containerP=[]

for i in lst_values:
    val=[abs(j-i) for j in lst_r]
    print(val)
    val.index(min(val))
    containerP.append(lst_r[val.index(min(val))])

OUT = containerP

Cordially
christian.stan

1 Like

Here’s my CPython solution for good measure:

valuesToMap = IN[0]
range = IN[1]
remapped = []
for v in valuesToMap:
    i = 0
    while True:
        if range[i]>=v:
            result = [range[i-1], range[i]]
            break
        i+=1
    dist= [abs(v-j) for j in result]
    if dist[0] == dist[1]: net = result[1]
    else: net = [i[1] for i in sorted(zip(dist,result))][0]
    remapped.append(net)
OUT = remapped

Works by iterating over the list of values until it finds a value which it is equal to or less than the current value, getting the item at the previous index, and taking the shortest of the two distances. There was an edge case around matching values (your doubled 0 value) which I used as a 1:1 setup, and I didn’t test for values outside the limits of the list.

There is one other solution based on geometry which might be worth considering, but I’ll let your brain think that over. :slight_smile:

1 Like

I don’t know if you were thinking of that for the geometric approach :wink:

cordially
christian.stan

1 Like

One method. Another would be to draw a surface on the YZ plane arrayed along the X axis and converted to a polysurface, create points along the X axis, use a closest point to node and pull the X value from there.

1 Like

I would not have thought of it to be honest, you scocht me, speaking of that it’s time to throw a little behind the collar (Apéro time)
have a nice (day: 0 night: 1)
cordially
christian.stan

1 Like

Hi @christian.stan & @jacob.small ,

What about this approach?

def round_to_nearest(input_list, target_list):
    if not isinstance(input_list, list):
        input_list = [input_list]

    result = [] 
    for num in input_list:
        nearest = min(target_list, key=lambda x: abs(x - num))
        result.append(nearest)

    if len(result) == 1:
        return result[0]
    
    return result

input_list = IN[0]
target_list = IN[1]

OUT = round_to_nearest(input_list, target_list)
2 Likes

Thank you Mr. Daan :wink:
cordially
christian.stan