Removing only one instance of a duplicate value from a list

Hello!

**remove**  (value)  **from**  (list of values)

This phrase removes every instance of the given value from the list. Example:

> let L be {3, 1, 4, 1, 5, 9, 2, 6, 5, 3}; 
> remove 1 from L;

results in L being {3, 4, 5, 9, 2, 6, 5, 3}. Ordinarily "remove 7 from L" would produce a run-time problem, since L does not contain the value 7, but using the "if present" option lets us off this: the phrase then does nothing if L does not contain the value to be removed.

Is there a simple idiomatic way to remove only one instance of a value from a list, rather than every instance?

The obvious issue here is that a list is ordered. If you want to preserve order, then there’s no clear indication which entry we should remove.

This is the easiest way I could find.


When play begins:
	let L be { 3, 1, 4, 1, 5, 9, 2, 6, 5, 3 };
	say L;
	say line break;
	remove entry (last occurrence of 5 in L) from L;
	say L.
	
To decide which number is first occurrence of (n - a number) in (L - a list of numbers):
	repeat with num running from 1 to the number of entries in L:
		if n is entry num in L, decide on num;
	decide on 0.

To decide which number is last occurrence of (n - a number) in (L - a list of numbers):
	let entry be 0;
	repeat with num running from 1 to the number of entries in L:
		if n is entry num in L, let entry be num;
	decide on entry.

There is a room.

Interestingly, it seems iterating numbers in reverse order (running from n to 1) doesn’t work the same way in I7. I didn’t expect that.

That makes sense; thank you!

I guess that, in a case where n=0, it’s desirable to have the repeat from 1 to n loop not execute at all instead of executing for 1 and then for 0?

I meant that reversing my first phrase (running from list length to 1) simply fails. I haven’t looked at the i6, but I assume it implements loop guard clauses using <=, and in that case, the behaviour makes sense.

That’s right.

You can easily define a loop with the opposite sign:

To repeat with (loopvar - nonexisting K variable) running from (v - arithmetic value of kind K) down to (w - K) begin -- end loop:
	(- for ( {loopvar}={v}: {loopvar}>={w}: {loopvar}-- )  -).
2 Likes

Right, I just meant that this seemed like desirable behavior! For instance, if we were trying this on an empty list, we would want the loop from (1 to number of entries in the list) to not run at all, instead of trying to run backwards from 1 to 0.

Which I think is what you said about loop guards, I just didn’t express myself clearly!

Without dipping into I6, a workaround for a “reversed” loop could be to do a substraction when accessing the elements of the list.

To decide which number is last occurrence of (N - a number) in (L - a list of numbers):
	let target be 0;
	repeat with I running from 0 to (number of entries in L - 1):
		now target is number of entries in L - I
		if N is entry target in L, decide on target;
	decide on 0.

(I renamed the variables because I was getting confused by their names.)

It’s a bit a more efficient that what Eleas proposed because we don’t need to iterate through the whole list. Zarf’s method is still a bit more efficient because it doesn’t have to do the substraction. But I guess all this optimisation is negligible anyway.