Code archives/Algorithms/filter a list using reflection
This code has been declared by its author to be Public Domain code.
Download source code
| |||||
I have an ambition of making a clever object database system. This is a first step towards that - a way of filtering a list of objects according to a set of conditions written as a string in normal blitzmax-ish notation. The idea is that the filter function returns a tquery object which you can either iterate through directly or keep for later. It only works out what belongs to the query when you evaluate it, so it's always up to date. | |||||
Rem filter lists! usage: the filter function works on any list or tquery object, and takes a condition string and returns a tquery which is a collection of all the objects in the list satisfying the condition conditions are pretty much as you write them in blitzmax, except I haven't made it do function calls yet EndRem 'shunting yard algorithm - turns normal notation into RPN for easier parsing Type shuntingyard Field in$ Field out$ Field ops$[][] Field token$[] Method parse$() While in nexttoken() Select token[0] Case "literal" output Case "function" push Case "," While ops[0][0]<>"(" pop If token[0]<>"(" output Wend Case "(" push Case ")" While pop()<>"(" output Wend If Len(ops) And ops[0][0]="function" pop output EndIf Default If token[0][..2]="op" Local otoken$[]=token op=Int(token[0][2..]) While Len(ops) And ops[0][0][..2]="op" And Int(ops[0][0][2..])<op pop output Wend token=otoken push EndIf End Select Wend While Len(ops) pop output Wend Return out End Method Method findnexttoken$() i=0 While i<Len(in) And in[i]=32 i:+1 Wend in=in[i..] i=0 While i<Len(in) Select Chr(in[i]) Case "!","&","|","=","<",">","(",")","," If i=0 t$=Chr(in[0]) in=in[1..] Return t Else t$=in[..i] in=in[i..] Return t EndIf Case " " t$=in[..i] in=in[i..] Return t End Select i:+1 Wend t$=in in="" Return t End Method Method nexttoken() tok$=findnexttoken() 'Print "TOK: "+tok Select tok Case "!","not" token=["op2",tok] Case "&","|","and","or" token=["op3",tok] Case "=","<",">" token=["op1",tok] Case "(" token=["(","("] Case ")" token=[")",")"] Case "," token=[",",","] Default If Len(in) And Chr(in[0])="(" token=["function",tok] 'in=in[1..] Else token=["literal",tok] EndIf End Select 'Print token[0]+" : "+token[1] End Method Method pop$() token=ops[0] ops=ops[1..] Return token[0] End Method Method push() ops=[token]+ops End Method Method output() out:+token[1]+" " End Method End Type Function shunt$(in$) s:shuntingyard=New shuntingyard s.in=in Return s.parse() End Function 'the important bit! call this with a list or a tquery, and a condition 'if you pass in a tquery, the new condition will be added to any previous ones. Function filter:tquery(o:Object,condition$) condition=Trim(shunt(condition)) 'Print condition Local bits$[]=condition.split(" ") Local stack:TList=New TList While i<Len(bits) Select bits[i] Case "!","not" c:tcondition=tcondition(stack.removelast()) stack.addlast notcondition.Create(c) Case "&","and" c1:tcondition=tcondition(stack.removelast()) c2:tcondition=tcondition(stack.removelast()) stack.addlast andcondition.Create(c1,c2) Case "|","or" c1:tcondition=tcondition(stack.removelast()) c2:tcondition=tcondition(stack.removelast()) stack.addlast orcondition.Create(c1,c2) Case "=" value$=String(stack.removelast()) fields$=String(stack.removelast()) 'Print fields+" = "+value stack.addlast eqcondition.Create(fields,value) Case "<" value$=String(stack.removelast()) fields$=String(stack.removelast()) 'Print fields+" = "+value stack.addlast ltcondition.Create(fields,Double(value)) Case ">" 'Print "EQUAL" value$=String(stack.removelast()) fields$=String(stack.removelast()) 'Print fields+" = "+value stack.addlast gtcondition.Create(fields,Double(value)) Default stack.addlast bits[i] End Select i:+1 Wend c:tcondition = tcondition(stack.removelast()) If tquery(o) Return tquery(o).addcondition(c) ElseIf TList(o) q:tquery=New tquery q.data=TList(o) q.condition=c Return q EndIf End Function 'object enumerator type for tquery Type queryenum Field obj:Object Field l:TLink Field query:tquery Function Create:queryenum(q:tquery) qe:queryenum=New queryenum qe.query=q qe.l=q.data.firstlink() Return qe End Function Method nextobject:Object() If obj d:Object=obj obj=Null Return d Else Return findnext() EndIf End Method Method hasnext() If obj Return True Else Return findnext()<>Null EndIf End Method Method findnext:Object() While l d:Object=l.value() l=l.nextlink() If query.contains(d) obj=d Return obj EndIf Wend Return Null End Method End Type 'represents a condition to be applied to a list of objects Type TQuery Field condition:tcondition Field data:TList Method objectenumerator:queryenum() qe:queryenum=queryenum.Create(Self) Return qe End Method Method contains(d:Object) 'If Not data.contains(d) Return false If Not condition Return True Return condition.appliesto(d) End Method Method addcondition:tquery(c:tcondition) qe:tquery=New tquery qe.data=data If condition qe.condition=andcondition.Create(condition,c) Else qe.condition=c EndIf Return qe End Method End Type Type tcondition Method appliesto(d:Object) Abstract End Type Type andcondition Extends tcondition Field c1:tcondition,c2:tcondition Function Create:andcondition(c1:tcondition,c2:tcondition) ac:andcondition=New andcondition ac.c1=c1 ac.c2=c2 Return ac End Function Method appliesto(d:Object) If c1.appliesto(d) And c2.appliesto(d) Return True Else Return False EndIf End Method End Type Type orcondition Extends tcondition Field c1:tcondition,c2:tcondition Function Create:orcondition(c1:tcondition,c2:tcondition) oc:orcondition=New orcondition oc.c1=c1 oc.c2=c2 Return oc End Function Method appliesto(d:Object) If c1.appliesto(d) Return True If c2.appliesto(d) Return True Return False End Method End Type Type notcondition Extends tcondition Field c:tcondition Function Create:notcondition(c:tcondition) oc:notcondition=New notcondition oc.c=c Return oc End Function Method appliesto(d:Object) Return (Not c.appliesto(d)) End Method End Type Type fieldcondition Extends tcondition Field fields$[] Method getfield:Object(d:Object) i=0 While i<Len(fields) d=TTypeId.ForObject(d).findField(fields[i]).get(d) i:+1 Wend Return d End Method End Type Type eqcondition Extends fieldcondition Field value$ Function Create:eqcondition(fields$,value$) ec:eqcondition=New eqcondition ec.fields=fields.split(".") ec.value=value Return ec End Function Method appliesto(d:Object) 'Print " ".join(fields)+" = "+value+"? "+(String(getfield(d))=value) Return String(getfield(d))=value End Method End Type Type ltcondition Extends fieldcondition Field value:Double Function Create:ltcondition(fields$,value:Double) lc:ltcondition=New ltcondition lc.fields=fields.split(".") lc.value=value Return lc End Function Method appliesto(d:Object) Return Double(String(getfield(d)))<value End Method End Type Type gtcondition Extends fieldcondition Field value:Double Function Create:gtcondition(fields$,value:Double) gc:gtcondition=New gtcondition gc.fields=fields.split(".") gc.value=value Return gc End Function Method appliesto(d:Object) Return Double(String(getfield(d)))>value End Method End Type 'example Type dude Field name$ Field number Function Create:dude(name$,number) d:dude=New dude d.name=name d.number=number Return d End Function End Type l:TList=New TList l.addlast dude.Create("Jim",1) l.addlast dude.Create("Mike",2) l.addlast dude.Create("Bubba",3) Print "DUDES~n------------" For d:dude=EachIn l Print d.number+": "+d.name Next Print "------------~n" Print "LAZY EVALUATION" condition$="number=1" q:tquery=filter(l,condition) Print "dudes where "+condition For d:dude=EachIn q Print d.name Next Print "~n(add 1: Wanda)~n" l.addlast dude.Create("Wanda",1) Print "dudes where "+condition For d:dude=EachIn q Print d.name Next Print "~n~nnow you try!~n" While 1 condition$=Input("condition> ") 'Print condition q:tquery=filter(l,condition) For d:dude=EachIn q Print d.number+": "+d.name Next Wend |
Comments
| ||
Nifty. |
Code Archives Forum