Reputation: 4338
I have a list of list which has 4 columns. Now I want to iterate over each list in list of list to find the first list which has say value ABC for first column and value
XYZ for 3rd col. If this criteria satisfy I want to get out of the loop and return what was in 4th col. E.g of how list would look like below.
`AAA 23 `BBB value
`AAA 23 `CCC value
`ABC 23 `XYZ value
`BBB 23 `CCC value
......
.....
I tried to do something like below, but this does not seem to work.
myfunc:{if [x[0] =`ABC and x[2]=`XYZ ; val:x[3] ; 'break ] }
@[myfunc;x; if [x[0] =`ABC and x[2]=`XYZ; :x[3]]] each ListOfList
Note that i do not want to traverse whole of list of list as it has millions of list in it, and is interested in fetching value of first list where criteria is matched.
Upvotes: 0
Views: 103
Reputation: 13657
If your concern is that the list is very big and you don't want to scan the entire list then this approach could suit (increment index by one so long as criteria not true)
q)l:((`AAA;23;`BBB;`value1);(`AAA;23;`CCC;`value2);(`ABC;23;`XYZ;`value3);(`BBB;23;`CCC;`value4))
q)l[{not`ABC`XYZ~l[x;0 2]}(1+)/0;3]
`value3
Upvotes: 2
Reputation: 713
You can use nested indexing to retrieve the first and third element of each nested list respectively and then find ?
to find the first occurrence. Note, find actually does exactly that, it returns the first match. https://code.kx.com/q/ref/find/
q)l:((`AAA;23;`BBB;`value);(`AAA;23;`CCC;`value);(`ABC;23;`XYZ;`value);(`BBB;23;`CCC;`value))
q)l
`AAA 23 `BBB `value
`AAA 23 `CCC `value
`ABC 23 `XYZ `value
`BBB 23 `CCC `value
q)l[::;0 2]
AAA BBB
AAA CCC
ABC XYZ
BBB CCC
q)l[::;0 2]?(`ABC;`XYZ)
2
q)l l[::;0 2]?(`ABC;`XYZ)
`ABC
23
`XYZ
`value
q)l2:l,l
q)l2
`AAA 23 `BBB `value
`AAA 23 `CCC `value
`ABC 23 `XYZ `value
`BBB 23 `CCC `value
`AAA 23 `BBB `value
`AAA 23 `CCC `value
`ABC 23 `XYZ `value
`BBB 23 `CCC `value
q)l2 l2[::;0 2]?(`ABC;`XYZ)
`ABC
23
`XYZ
`value
q)\ts:1000 l2 l2[::;0 2]?(`ABC;`XYZ)
1 1696
Alternatively, if you're looking for all occurrences (easily adjustable to first) you can create a table and then use below syntax
q)select from (`a`b`c`d!/:l2) where ([] a;c) in ([] a:enlist `ABC;c:`XYZ)
a b c d
----------------
ABC 23 XYZ value
ABC 23 XYZ value
just the first
q)first select from (`a`b`c`d!/:l2) where ([] a;c) in ([] a:enlist `ABC;c:`XYZ)
a| `ABC
b| 23
c| `XYZ
d| `value
Upvotes: 2
Reputation: 3786
Maurice answer suggesting using efficient vector comparisons makes sense.
As a general structure holding data in tables can lead to more efficient and readable queries compared to working on nested lists:
q)l:((`AAA;23;`BBB;`value1);(`AAA;23;`CCC;`value2);(`ABC;23;`XYZ;`value3);(`BBB;23;`CCC;`value4))
q)t
x y z k
-----------------
AAA 23 BBB value1
AAA 23 CCC value2
ABC 23 XYZ value3
BBB 23 CCC value4
q)exec first k from t where x=`ABC,z=`XYZ
`value3
Upvotes: 1
Reputation: 883
If all the list within the list has at least 4 entries then it might be more efficient to perform a vector comparison rather than using an iterator.
See: https://code.kx.com/q4m3/3_Lists/#310-elided-indices
list[;3]where(list[;0]=`ABC)&list[;2]=`XYZ / to get all the matching entries
list[;3]first where(list[;0]=`ABC)&list[;2]=`XYZ / to get only the first match
If you want to use a while loop:
{while[not[`ABC`XYZ~x[0:0 2]]&count x;x:1_x];x[0;3]}list
In your solutions, there are a few syntax errors.
Upvotes: 2