Remove subitem from association list

Started by cormullion, July 20, 2009, 02:19:31 PM

Previous topic - Next topic

cormullion

Given an association list:


(set 'al '(
  ("A" 123 "fred" 2)
  ("B" 234 "jim" 3)
  ("C" 345 "arthur" 4)
  ("D" 456 "dave" 5)))


how would I remove the "fred" "jim" "arthur" and "dave" strings? In fact, I don't want to remove them by referring to their content, but by specifying index 2. It doesn't seem to be as easy as it could be. (Or is it?!)

Lutz

#1
There are two ways to do this. The first method pops from each sub-list and is only possible since v.10.0:


; remove the element at offset 2 in each sublist
(dotimes (i (length al))
(pop (al i) 2))

al => (("A" 123 2) ("B" 234 3) ("C" 345 4) ("D" 456 5))


The second way treats 'al' as one nested list using two indexes to address the element to be popped from 'al':


; remove at i 2 of al
(dotimes (i (length al))
  (pop al i 2))

; remove at (list i 2) of al
(dotimes (i (length al))
  (pop al (list i 2)))


The second time the indexes are in a vector, similar to what 'ref' would return in


(ref "fred" al) => (0 2)

Lutz

#2
... and yet another method addressing the sublists as associations:


(pop (assoc "A" al) 2) => "fred"

cormullion

#3
cool. thanks! I had been trying replace and set-ref-all - to avoid looping ...

Lutz

#4
Quoteavoid looping ...


you could use 'replace' with 'match':


(replace '(*) al (append (0 2 $it) (-1 $it)) match)

al => (("A" 123 2) ("B" 234 3) ("C" 345 4) ("D" 456 5))


you could do similar with 'set-ref-all' but I suspect that this is faster. The match expression will match every top-level list in 'al', which are the sub-lists in $it. $it is not writable, so we have to construct the new sublist appending slices.



Likewise you could do:


(set 'al (map (fn (e) (append (0 2 e) (-1 e))) al))

but I guess your were looking for an in-place replacement.

cormullion

#5
Yes, they all work! I like the powerful single line style. The more you look, the more you find:


(replace '(*) al (list (select $it '(0 1 3))) match)

thanks!

Lutz

#6
... and you don't need 'list' and the selection indexes don't need to be in a list (but they can be, if you wish):


(replace '(*) al (select $it 0 1 3) match)

so that is probably the shortest solution, we can come up with.

newdep

#7
Yes true you have to look because its not that obvious actualy..



something that I would use instantly is a combination of 'map

together with 'pop.. but its not going to work..



but this is..


(map (fn(x) (replace (x 2) x)) al)

is a short solution and returns the result but its a non-destructive solution..
-- (define? (Cornflakes))

newdep

#8
oke oke and there is the good old loop (hello python)


(for (x 0 (dec (length al))) (pop (al x) 2) )

probably the fastest..
-- (define? (Cornflakes))

Lutz

#9
but we had a better one like this already (first solution in this thread):


(dotimes (i (length al))
   (pop (al i) 2))