# Extracting values from a list

I had already solved this problem, but in an unnecessarily convoluted way that seems too specific. Can this be done better?

From a list:

(8 "layer-name" 10 x1 20 y1 10 x2 20 y2 10 x3 20 y3)

8, 10 and 20 are the "key-words" for layer-name, x-coord and y-coord respectively. The actual values follow the "key-words".

Return values should be --> '(("layer-name" x1 y1 x2 y2) ("layer-name" x2 y2 x3 y3))

#lang racket

(require srfi/1)

(define test-list '("multiple-lines" 8 "layer-name" 10 30 20 30   10 40 20 40   10 50 20 50))

(define (get-values lst lst2)
(cond ((eq? lst2 '())
'())
(else
(cons (cadr (member (car lst2) lst))
(get-values lst (cdr lst2))))))

(define (get-multiple-values lst key key2) ; key is single values. key2 is repeatable values
(define (splitter lst keyword) ; (splitter '(10 x1 20 y1 10 x2 20 y2 10 x3 20 y3) 10 20)  --->  '((x1 y1 x2 y2 x3 y3) (x2 y2 x3 y3) (x3 y3))
(cond ((not (member keyword lst)) '())
(else (cons (member keyword lst) (splitter (cddr (member keyword lst)) keyword)))))
(define (joiner lst lst-of-lsts) ; (joiner '(1 2) '((a b) (c d)))  --->  '((1 2 a b) (1 2 c d))
(cond ((empty? lst-of-lsts) '())
(else (cons (append lst (car lst-of-lsts))
(joiner lst (rest lst-of-lsts))))))
(define (trimmer size lst-of-lsts) ; (trimmer  '((x1 y1 x2 y2 x3 y3) (x2 y2 x3 y3) (x3 y3)))
(cond ((empty? lst-of-lsts) '())
((> size (length (car lst-of-lsts)))
(trimmer size (rest lst-of-lsts)))
(else (cons (take (car lst-of-lsts) size)
(trimmer size (rest lst-of-lsts))))))
(joiner (get-values lst key)
(trimmer 4 (map
(lambda (lst) (remq* key2 lst))
(splitter lst (first key2))))))

> '(("layer-name" 30 30 40 40) ("layer-name" 40 40 50 50))


I have a strong suspicion that this is an XY Problem, or at the very least, that you're using the wrong abstraction and/or data structure for representing data, using numbers as keywords somehow doesn't seem right. Anyway, I think the requested functionality can be implemented in an alternate, simpler way:

; obtain all values for a given keyword
(define (key-values lst kwd)
(cond ((null? lst) '())
((equal? (first lst) kwd)
(cons (second lst) (key-values (cddr lst) kwd)))
(else (key-values (cddr lst) kwd))))

; interleave elements from two lists
(define (interleave lst1 lst2)
(cond ((null? lst1) lst2)
((null? lst2) lst1)
(else (cons (first lst1) (interleave lst2 (rest lst1))))))

; create all layers according to the defined structure
(define (joiner name lst1 lst2)
(let loop ((lst (interleave lst1 lst2)))
(if (null? (cddr lst))
'()
(cons (list name (first lst) (second lst) (third lst) (fourth lst))
(loop (cddr lst))))))

; main function, receives a list of values and a list of keywords
(define (get-values lst kwds)
(let ((name (first (key-values lst (first kwds))))
(xs   (key-values lst (second kwds)))
(ys   (key-values lst (third kwds))))
(joiner name xs ys)))


For example:

(define test-list '(8 "layer-name" 10 30 20 30 10 40 20 40 10 50 20 50))
(define keywords '(8 10 20))
(get-values test-list keywords)
=> '(("layer-name" 30 30 40 40) ("layer-name" 40 40 50 50))

• Is it really a XY problem? TO give more context, i am reading DXF files. The output stream from the DXF file is a list of strings, and the format is a keyword-number anywhere from 0 to 329, which specifies the type of the following value. Perhaps i should create a general function that reads the entire list of strings and converts the values into their proper type from a string depending on the keyword?
– KRC
Dec 12, 2014 at 0:24
• @krc that might be a good idea, I'm not familiar with that file format, but I'd suggest that first you look for an existing library to do the job, or perhaps a generic parser for reading streams with a given format. Anyway, the question in the comment doesn't resemble the original question, so it's up to you if you deem my answer useful. You should have explained the real situation from the beginning, this was indeed and XY problem. Dec 12, 2014 at 2:09
• @ÓscarLópez Some Context: DXF is the file format used by AutoCad. It is a text file representation of a more compact binary format, DWG. People have been using Lisps to manipulate it since 1986 when AutoLisp was introduced. The rough analogy I like is that AutoCad is Emacs for vectors and by extension carries similar historical baggage. Anyway, the question is a bit of an iceberg and there's a meaningful mass under the surface. Dec 29, 2014 at 19:16