Entries in vs (2)

How do I split a list?

If you want to split the list into lists of equal size, see this related faq on the # (take) operator.

If you want to split a string on a delimiter, use the vs (vector from scalar) function:

q)" " vs "The quick brown fox"
q)", " vs "Hello, world!"

When its left argument is the null symbol, `, the vs function breaks apart a symbol on dots:

q)` vs `foo.bar.baz

Those are the most common cases. We can also split a list into lists of varying length by passing a list of indexes as the left argument to the _ (cut) operator:

q)0 1 4 9 _ til 10
1 2 3
4 5 6 7 8

To split a list of some type other than char using a delimiter is a little more complicated. We start by finding the indexes in the list that match the delimiter:

q)list: 1 2 0 3 4 0 5 6
q)delimiter: 0
q)indexes: where delimiter = list
2 5

Now we can break list into pieces using the _ (cut) operator as above:

q)indexes _ list
0 3 4
0 5 6

This is almost what we want. We'll use _/: (drop-each-right) to get rid of the delimiters:

q)1 _/: indexes _ list
3 4
5 6

We can grab the first element of the result with # (take):

q)first[indexes] # list
1 2

Then we can just join (using ,) the two together:

q)(enlist first[indexes] # list), 1 _/: indexes _ list
1 2
3 4
5 6

Note that we must call enlist on the front of the list or else we'll get something a little different from what we intended:

q)(first[indexes] # list), 1 _/: indexes _ list
3 4
5 6

Lastly, we can generalize to non-atomic types by replacing = with ~/: (match-each-right):

split: {[list; delimiter]
    indexes: where delimiter ~/: list;
    front: first[indexes] # list;
    rest: 1 _/: indexes _ list;
    : (enlist front), rest;

How do I parse a ctrl-A delimited string?

Short answer: "\001" vs data

Use the vs (vector from scalar) function:

q)"\001" vs fstring

We can combine vs with /: (each right) to break up each component:

q)"=" vs/: "\001" vs fstring
"f1" "va"
"f2" "vb"
"f3" "vc"

Note that you cannot put any space between vs and /:. If you did, that would be a comment.

We might next turn fstring's contents into a table using a combination of flip and ! (dict):

q)flip "=" vs/: "\001" vs fstring
"f1" "f2" "f3"
"va" "vb" "vc"
q)`field`value ! flip "=" vs/: "\001" vs fstring
field| "f1" "f2" "f3"
value| "va" "vb" "vc"
q)flip `field`value ! flip "=" vs/: "\001" vs fstring
field value
"f1"  "va" 
"f2"  "vb" 
"f3"  "vc"

This is almost what we want. Usually, though, you want the field names to be symbols rather than strings. We can do that by applying (@) the $ (cast) operator to (only) the values of the field column:

q)columns: @[flip "=" vs/: "\001" vs fstring; 0; `$]
f1   f2   f3  
"va" "vb" "vc"
q)flip `field`value ! columns
field value
f1    "va" 
f2    "vb" 
f3    "vc" 

However, there is a shortcut we can take to deconstruct the string into a table using one of the many variants of 0::

q)flip `field`value ! "S=\001" 0: fstring
field value
f1 "va"
f2 "vb"
f3 "vc"

Not only is this last example more succinct, it's much faster:

q)\t do[100000;
        flip `field`value !
                 @[flip "=" vs/: "\001" vs fstring; 0; `$]]
q)\t do[100000; flip `field`value ! "S=\001" 0: fstring]

See also How do I build a ctrl-A delimited string?