removing a specific element in a list without removing repetitions of it

Max Burnell's icon

I am currently trying to build a device which will randomly generate a string of 8 numbers, the user can then select the number they would like to move to the beginning of the string.

This all works perfectly using zl.nth & zl.filter. However, if I randomly generate a string which has two or more of the same number and select one of them as the number I'd like to move to the start of the string, zl.filter removes all instances of it.

I have thought of using 'urn' to generate my random numbers so as not to get repetitions, however this does not solve the issue if the numbers were to be set by a user.

I have also tried using various configurations of zl.slice however these tend to get over complicated and not work when feeding the changed string back into it.

any insight would be amazing!

example of current function:
https://youtu.be/sDzdSkZ_qVM

Andy Maskell's icon

I thought that this was going to be quite simple until I had a go at it. I suspect that there are more elegant ways of doing it but this is my first working attempt!

It looks complicated but the sequence of events is all important. It also doesn't help that three different clear messages (clear, set and zlclear) are required to initialise different objects!

On hitting the bang at the top, everything is cleared and initialised. The uzi then generates 8 random numbers and inserts them into an embedded coll object as well as the top results message box. On selecting an index number with the number selector on the left, the trigger starts an ordered sequence of events. Firstly, the top results box is copied to the bottom for comparison. The chosen value in the coll is re-added to the coll with an index value of zero. Then the original value is deleted. Next, the coll is sorted by index numbers moving the chosen value to the front. Finally the coll is renumbered from one and its contents dumped out into the top result message box.

It works and does what you described but it looks a bit more complex than I'd like. I'm sure that there are lots of other ways of doing this with a variety of zl objects so others might well come up with something more sophisticated!

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

Max Burnell's icon

Amazing man! It does sound super simple doesn't it but I have really been racking my brain over it these last couple days! Your patch does it perfectly. Will further investigate ways to do something similar with zl objects!

Thanks for the explanation of your patch too! Lovely stuff Andy.

Roman Thilenius's icon

removing a specific element... basically...

1.)
write in coll, use delete command, read from coll

or
2.)
zl slice 7 -> zl slice 1 -> zl join

or why not
3.)
uzi 10 -> route 6 -> "1, 2, 3, 4, 5, 7, 8, 9, 10" -> zl nth -> zl group 9

Andy Maskell's icon

It's not just about removing an element in the list - it's about pulling that element out and repositioning it at the front. I'm sure it could be done with a succession of zl objects but right now I can't quite get my head round it.

I've tweaked with my patch a bit to give it a more aesthetic way of selecting the item to move:

The unchanged list will be in the red box and the updated list is in the green box. Once loaded, you can keep selecting a new item to go to the top.

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

Andy Maskell's icon

I'm just waiting for @11Olsen to come along and show how it can be done in three objects! 😂🤣😂

Roman Thilenius's icon


okay, least number of objects battle, let´s go.

among my 3 approaches, the slice solution wins easily, with 7 processes & 11 messages.

in zl-only solutions the whole "reordering" (or cutting-, pasting-, repeating-) processes can be based on a single trigger.

unless we need to repeat such operations in a loop, the extra work of making index numbers and using coll, table, or zl nth can be omitted.

if the list length is unknown then use 2x join instead of the group. (still 1 connection less than measuring len at the beginning.)


in real life i would rather make an abstraction for "move one list element´s position" even when it means more code, and then call [110.elem.move 5 1].

Max Burnell's icon

The concept is for a sequencer, so ideally we'd be able to feed the message with the shifted integer back into the function and repeat it as many times as we'd like.

short example of its function;
https://youtu.be/sDzdSkZ_qVM

my inital patch:

Andy Maskell's icon

Ah well @Roman, I could be cheeky and suggest that I'm only using 8 objects in the red box to do the order processing as the part in the green box is generating the random sequence and the part in the blue box is the selection bit:

I hope you've noticed that I'm now using triggers to control the sequenceing rather than my old method of endless bangs, delays and pipes! 😂🤣😂

Roman Thilenius's icon


yes yours is also only 8 when the length is known, but you have made more connections. (and of course coll and messagebox are expensive stuff - but we had yet to check how expensive zl´s are in comparison.)

6 anyone?

...

so... but when he actually wants to perform the same process multiple times from the same scheduler bang in a for loop, a solution using indexes might be as good, because then we can accumulate the different processes for the indexes only and later create the list in one go.

Roman Thilenius's icon


the basic idea behind the slice/join solutions is to consider the single element as a part of the list, then perform cut/copy/paste processes to these parts.


the basic idea behind using indexes is that you can use arithmetic processes on them.

these could then in some situations be combined to a single process instead of putting 3 of the operations in series.

right now i am not able to provide a useful example though.

it is probably more useful when you do not want to loop the same, but use many different operations in series.

11OLSEN's icon

If I understood correctly:

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

Roman Thilenius's icon


thanks to moving the index processing into an external, we´re down to only 8 connections now. (fixed length situation )

but unfortunately it is still 7 objects.

because for some reason [vexpr] does not like to talk to [t l "1 2 3 4 5 6 7 8"]

:(


Roman Thilenius's icon


addendum/correction.

of course you would not need to recalculate that index stuff until it changes. so that is not (always) part of the routine.

ladies and gentlemen: reordering list elements by index using 6 objects, 7 connections.



Max Burnell's icon

I can't believe I didn't read what the right outlet of the zl.nth does @11Olsen , that would've saved me so much pain. Thanks for all the replies though fellas, I have learnt so much and at the same time realised how little I still know about this program lmao!

You're doing wonderful work!

Roman Thilenius's icon


+++ brand new 3-objects solution disqualified by jury +++
+++ jockey 11Olsen and his horse to be convicted of doping +++

/fake news

11OLSEN's icon
Andy Maskell's icon

I think that's 2 objects, 5 connectors and a nice interface! Do I win?! 😂🤣😂

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

Edit! Sorry, that should be a random 100.

Andy Maskell's icon

Oh, and a bonus mark for presentation!

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

Iain Duncan's icon

Oh this is a fun problem and a perfect Scheme use case. It took me more tries than I expected! Here is my Scheme for Max solution. I might even put this in the cookbook. Let me know if you want a different workflow, it should be easy to adjust. (In the interest of clarity I didn't go for lowest number of objects. You could remove 5 of them for a total of 6 if you wanted a harder to read patch.)

patch
; contents of move-once.scm
(define (output-random-list)
  (out 1 (map (lambda(x)(random 9)) (make-list 8))))

(define (move-once value . list-arg)
  "output a list with a value moved to the front, or unchanged if not present"
  (define (process-list front rest)
    (cond 
      ((null? rest) #f ); value was not found
      ((= value (car rest))
        (append front (cdr rest)))
      (else
        (process-list (append front (list (car rest))) (cdr rest)))))
  (let* ((result (process-list '() list-arg))
         (out-list (if result (cons value result) list-arg)))
    (out 0 out-list)))
Iain Duncan's icon

Version optimized for fewest Max objects:

(define (output-random-list)
  (out 1 (map (lambda(x)(random 9)) (make-list 8))))

(define (f-list list-arg)
  "output a list with a value moved to the front, or unchanged if not present"
  (let ((value (car list-arg))
        (list-in (cdr list-arg)))
    (define (process-list front rest)
      (cond 
        ((null? rest) #f ); value was not found
        ((= value (car rest))
          (append front (cdr rest)))
        (else
          (process-list (append front (list (car rest))) (cdr rest)))))
    (let* ((result (process-list '() list-in))
           (out-list (if result (cons value result) list-arg)))
      (out 0 out-list))))
11OLSEN's icon

@IAIN I think the OP wants to address the number by index. How would you bring the 2 which is item 7 to the front?

Iain Duncan's icon

Oh ok, that's a lot easier. Here is the "readable version". You could do this a lot of ways, but this is easy to follow. It's an iterative algorithm coded recursively with named-let.

(define (output-random-list)
  (out 1 (map (lambda(x)(random 9)) (make-list 8))))

(define (move-once index . list-input)
  (let process-loop ((i 0) (front '()) (rest list-input))
    (if (= i index) 
      (out 0 (cons (car rest) (append front (cdr rest))))
      (process-loop (+ 1 i) (append front (list (car rest))) (cdr rest)))))
Iain Duncan's icon

And here is the "lowest number of objects version...

(define (output-random-list)
  (out 1 (map (lambda(x)(random 9)) (make-list 8))))

(define (f-list list-arg)
  (let ((index (car list-arg))
        (list-input (cdr list-arg)))
    (let process-loop ((i 0) (front '()) (rest list-input))
      (if (= i index) 
        (out 0 (cons (car rest) (append front (cdr rest))))
        (process-loop (+ 1 i) ; have to break line here for this width, grr
          (append front (list (car rest))) (cdr rest))))))
Roman Thilenius's icon


not to question the legitimation of your trophy, but of course the solution based on the second outlet of zl nth really only works for single elements - for the specific application asked here it could not be more perfect.
as soon as you want to isolate and move 2 or 3 elements all other 15 approaches will be more flexible and more "generic".

and while scheme, javascript or gen are out of competition, it is always interesting to see how things work in other languages, that often leads to new ideas about the problem.

Andy Maskell's icon

Sorry @Roman, who are you awarding the trophy to?

Iain Duncan's icon

Clearly recursion with intuitive function names like cons, car, and cdr is the most readable. Clearly! ;-)

Roman Thilenius's icon


lol, the trophy seems to be still in posession of 11Olsen.

proper use of triggers is mandatory and solution with less than 3 objects is highly suspicious!

no wait, what am i talking. that is actually only 2 objects he uses. (the demo patch is a bit strange)

Roman Thilenius's icon


nth and slice based element-isolation is so long useful as long as we dont need to do it for more than one element.

remove nth (1 object)
move nth to the beginning (2 objects)
move nth to end (3 objects)
move nth to position (4 objects)
insert x at position (3 objects)
replace nth with x (4 objects)
exchange nth with position (11 objects, same as my index-based generic reorderer above)

11OLSEN's icon

move nth to end (2 objects)

replace nth with x (1 object)

Could we stop to blow up this thread? I'm sure there will be another chance for a trophy soon...

Andy Maskell's icon

What started out as a seemingly straightforward task became more of a challenge only to end up with the simplest of solutions. I knew that you crack it @11Olsen!

Roman Thilenius's icon


great addition, 11Olsen, but wont work in my version of max ;)

eventually some of the othes can also be made easier using the later zl additions or things like substitute or prepend.