vexpr and finding the closest number in a list

oolfur's icon

hi

somewhere on this forum i found a great way to find a number in a list closest to x.
lets say i have a list of the numbers [3 10 30 100] and my integer is 25, the output will be 30.

it looks like this::

{
    "patcher" :     {
        "fileversion" : 1,
        "appversion" :         {
            "major" : 6,
            "minor" : 1,
            "revision" : 2,
            "architecture" : "x86"
        }
,
        "rect" : [ 75.0, 119.0, 640.0, 480.0 ],
        "bglocked" : 0,
        "openinpresentation" : 0,
        "default_fontsize" : 9.0,
        "default_fontface" : 0,
        "default_fontname" : "Helvetica Neue",
        "gridonopen" : 0,
        "gridsize" : [ 15.0, 15.0 ],
        "gridsnaponopen" : 0,
        "statusbarvisible" : 2,
        "toolbarvisible" : 1,
        "boxanimatetime" : 200,
        "imprint" : 0,
        "enablehscroll" : 1,
        "enablevscroll" : 1,
        "devicewidth" : 0.0,
        "description" : "",
        "digest" : "",
        "tags" : "",
        "boxes" : [             {
                "box" :                 {
                    "fontname" : "Helvetica Neue",
                    "fontsize" : 9.0,
                    "frgb" : 0.0,
                    "id" : "obj-34",
                    "maxclass" : "comment",
                    "numinlets" : 1,
                    "numoutlets" : 0,
                    "patching_rect" : [ 49.0, 22.0, 150.0, 17.0 ],
                    "text" : "compare"
                }

            }
,             {
                "box" :                 {
                    "fontname" : "Helvetica Neue",
                    "fontsize" : 9.0,
                    "frgb" : 0.0,
                    "id" : "obj-32",
                    "maxclass" : "comment",
                    "numinlets" : 1,
                    "numoutlets" : 0,
                    "patching_rect" : [ 128.0, 22.0, 150.0, 17.0 ],
                    "text" : "list"
                }

            }
,             {
                "box" :                 {
                    "comment" : "",
                    "id" : "obj-7",
                    "maxclass" : "outlet",
                    "numinlets" : 1,
                    "numoutlets" : 0,
                    "patching_rect" : [ 78.0, 219.0, 25.0, 25.0 ]
                }

            }
,             {
                "box" :                 {
                    "fontname" : "Helvetica Neue",
                    "fontsize" : 9.0,
                    "id" : "obj-6",
                    "maxclass" : "newobj",
                    "numinlets" : 2,
                    "numoutlets" : 2,
                    "outlettype" : [ "", "" ],
                    "patching_rect" : [ 78.0, 192.0, 69.0, 17.0 ],
                    "text" : "zl 1000 lookup"
                }

            }
,             {
                "box" :                 {
                    "fontname" : "Helvetica Neue",
                    "fontsize" : 9.0,
                    "id" : "obj-5",
                    "maxclass" : "newobj",
                    "numinlets" : 2,
                    "numoutlets" : 2,
                    "outlettype" : [ "int", "int" ],
                    "patching_rect" : [ 49.0, 135.0, 48.0, 17.0 ],
                    "text" : "minimum"
                }

            }
,             {
                "box" :                 {
                    "fontname" : "Helvetica Neue",
                    "fontsize" : 9.0,
                    "id" : "obj-4",
                    "maxclass" : "newobj",
                    "numinlets" : 1,
                    "numoutlets" : 2,
                    "outlettype" : [ "", "" ],
                    "patching_rect" : [ 128.0, 72.0, 69.0, 17.0 ],
                    "text" : "t l l"
                }

            }
,             {
                "box" :                 {
                    "fontname" : "Helvetica Neue",
                    "fontsize" : 9.0,
                    "id" : "obj-3",
                    "maxclass" : "newobj",
                    "numinlets" : 2,
                    "numoutlets" : 1,
                    "outlettype" : [ "" ],
                    "patching_rect" : [ 49.0, 99.0, 148.0, 17.0 ],
                    "text" : "vexpr abs($f2-$f1) @scalarmode 1"
                }

            }
,             {
                "box" :                 {
                    "comment" : "",
                    "id" : "obj-2",
                    "maxclass" : "inlet",
                    "numinlets" : 0,
                    "numoutlets" : 1,
                    "outlettype" : [ "" ],
                    "patching_rect" : [ 128.0, 39.0, 25.0, 25.0 ]
                }

            }
,             {
                "box" :                 {
                    "comment" : "",
                    "id" : "obj-1",
                    "maxclass" : "inlet",
                    "numinlets" : 0,
                    "numoutlets" : 1,
                    "outlettype" : [ "" ],
                    "patching_rect" : [ 49.0, 39.0, 25.0, 25.0 ]
                }

            }
],
        "lines" : [             {
                "patchline" :                 {
                    "destination" : [ "obj-3", 0 ],
                    "disabled" : 0,
                    "hidden" : 0,
                    "source" : [ "obj-1", 0 ]
                }

            }
,             {
                "patchline" :                 {
                    "destination" : [ "obj-4", 0 ],
                    "disabled" : 0,
                    "hidden" : 0,
                    "source" : [ "obj-2", 0 ]
                }

            }
,             {
                "patchline" :                 {
                    "destination" : [ "obj-5", 0 ],
                    "disabled" : 0,
                    "hidden" : 0,
                    "source" : [ "obj-3", 0 ]
                }

            }
,             {
                "patchline" :                 {
                    "destination" : [ "obj-3", 1 ],
                    "disabled" : 0,
                    "hidden" : 0,
                    "source" : [ "obj-4", 1 ]
                }

            }
,             {
                "patchline" :                 {
                    "destination" : [ "obj-6", 1 ],
                    "disabled" : 0,
                    "hidden" : 0,
                    "source" : [ "obj-4", 0 ]
                }

            }
,             {
                "patchline" :                 {
                    "destination" : [ "obj-6", 0 ],
                    "disabled" : 0,
                    "hidden" : 0,
                    "source" : [ "obj-5", 1 ]
                }

            }
,             {
                "patchline" :                 {
                    "destination" : [ "obj-7", 0 ],
                    "disabled" : 0,
                    "hidden" : 0,
                    "source" : [ "obj-6", 0 ]
                }

            }
],
        "dependency_cache" : [ ]
    }

}

however - what i need to do is have the output find the nearest number in the list, OTHER than x (itself). say i would have the list [3 10 30 100] and my integer is 30, the output would be 10 rather than 30 (i am making chords, thats why i dont need two of the same numbers).

any thoughts on this would be greatly appreciated! and you will receive 350 EXP.

thanks!
úlfur

oolfur's icon

basically, i need something that can find the number larger than x and closest to x. similarily, find the number smaller than xand closest to x.

im not good with code, but maybe a js object could do the math?

Roman Thilenius's icon

for experience points i´d do almost everything.

i would probably

1.
check if X is contained in the incoming chord (expr $f1==$f2 @scalar)

2.
and if yes, remove the 30 from the chord (using zl union or zl nth or something like that)

3. then do what you already have

p.s. using copy compressed when posting patches to the forum will help to get more answers

whoa, level up :)

metamax's icon

All you need is one extra object at top. Use [zl filter].

Keep in mind, this solution will chose the first nearest non-match in the list if the high and low have the same abs value. i.e. (10, 20, 30) chose 20 returns 10.

Max Patch
Copy patch and select New From Clipboard in Max.

metamax's icon

to share your patch:

Max window:
edit -> select all
edit -> copy compressed

Forum window:
edit -> paste
(don't use the code button)

oolfur's icon

thanks guys! metamax, your solution works beautifully.

now - if i wanted to get the abstraction to return only larger than numbers and closest to the input - how would i go about doing that?

metamax's icon

reverse the filtered list with [zl rev].

-

Max Patch
Copy patch and select New From Clipboard in Max.

oolfur's icon

dear metamax,

Max Patch
Copy patch and select New From Clipboard in Max.

somehow its not working for me, the returned number alternates between nearest high and low...

metamax's icon

I think I misunderstood you. The last patch I posted works exactly as intended. If the nearest high and low numbers have the same absolute value.. i.e. the same distance from the input - then you can choose which one to output. Otherwise, if there is no input match, it works exactly the same, outputting the nearest number in the list.

Max Patch
Copy patch and select New From Clipboard in Max.

-
-

If you only want higher values returned, that's even easier.. but there are a few different ways to handle input values that don't match any list elements.

Max Patch
Copy patch and select New From Clipboard in Max.

-

kjelgaard's icon

simple and beautiful. wish I had seen the original thread a month ago before I drove myself crazy doing the same thing for a sequencer.

oolfur's icon

dear metamax

my patch is almost working right now...

i am able to make triads with your function - and it works perfectly if i slide my mouse up and down the keyboard.

however - i cannot for the life of me figure out why it doesn't work if i "jump" between notes (make leaps with random intervals) . it very often gives me the wrong harmonic notes (they seem to stay put).

please have a look at my patch if you are interested, any help would be greatly appreciated!

triad.zip
zip
oolfur's icon

anyone?

metamax's icon

oolfur, you didn't reply to my last post so it's not clear what you originally wanted. Your question morphed from finding the closest number in a list to I honestly don't know what. The patch you just attached doesn't resemble anything I posted.. including [p closest].. so I don't know what you are comparing. I also don't know what you are trying to achieve in the bigger picture.

Perhaps if you strip your patch down to bare bones.. no unnecessary elements.. the problem will be more apparent. I did notice that you have 3 ksliders stacked on one another with one of them outputing values to two separate subpatchers that are returning values back to all three ksliders.. which are in turn are outputting values to three separate oscillators. Aside from the logic problems within the sub patchers, you might consider setting the two ksliders that are not sending values to the subpatchers to 'ignore click'. As it is now, you are sending output data from all three sliders to the oscillators before the subpatchers are returning values back to the ksliders.

There are other problems as well, but I'm not going to sit here and deconstruct it all for you. You are the man for that job.. Like I said, start by simplifying everything. It will make it all easier for you.. and if you can't figure it out, more people will be inclined to respond to your question.

Also.. you don't need to attach a .txt file for your coll values. You can select 'save data in patcher' in the coll inspector. That way, you don't need to attach anything... which likely will invite more people to look at your patch.

Good luck.

Sébastien's icon

HI MetaMax,

How would you go about this?
if there is a match output the match
if there is no match output the closest value whether its lower or higher

Thanks

metamax's icon

This seems to do the trick.

Max Patch
Copy patch and select New From Clipboard in Max.

Sébastien's icon

Hi METAMAX,
Neat and simple.
Thanks again!

Sébastien's icon

Hi METAMAX,

I am trying to achieve something similar but with a jit.matrix and jit.expr. I am stuck at first level expression with the @expr. Any tips?

Best,

colorscale_expr.maxpat
Max Patch
metamax's icon

Sébastien, [jit.expr] is similar to [expr] but standard [zl] objects don't really have an equivalent in jitter. You'll need to play with some other approaches to get everything working. Be sure to check out [jit.bsort], [jit.planeop] and [jit.op]. If you have any mathematical formulas you want to translate into [jit.expr] or [expr], I'll be glad to help. You may also want to look into [jit.gen] or [jit.pix] for even more options.

In the mean time, if you are trying to find 'nearest numbers' to figure out 'closest colors', there may be an easier approach. Colors can be described as coordinates in 3d space. The shorter the distance between two colors, the more similar they should be. The simplest formula to calculate color difference can be found here: https://en.wikipedia.org/wiki/Color_difference#CIE76

Here is a working example using [jit.expr]. Hope this helps. Good luck with your project!

Max Patch
Copy patch and select New From Clipboard in Max.

Sébastien's icon

Hi METAMAX,

That is elegant!

Thanks