Collections Functions - Gnorion/BizVR GitHub Wiki
- Creating Named and Dynamic Collections
- Decision Tables and Collections
- Processing Collections With Decision Tables
- Sep 2015 Collections of Cars
- Tutorial Testing with a collection of inputs
There are three basic collections.
- The basic collection is often called a bag. It stores objects with no ordering of the objects and no restrictions on them.
- Another unstructured collection is a set where repeated objects are not permitted: it holds at most one copy of each item. A set is often from a predefined universe.
- A collection where there is an ordering is often called a list. Specific examples include an array, a vector and a sequence.
- Collections are a bunch of objects that can be named and referred to.
- Different types of object can be grouped into a single collection.
- Objects in a collection can be basic data types, or structured data types or even collections.
- Special operations are available for processing collections
- The members of a collection do not even have to be of the same type.
example
{'red','red',3.24,'2022/12/31',T,{1,2,{'a','b',5}},'red'}
is a collection
- Sets are collections of objects that contain no duplicates
- The objects in a set are called its elements or members.
- The elements in a set can be any types of objects, including sets!
- The members of a set do not even have to be of the same type.
Example
{'red',3.24,'2022/12/31',T,{1,2,{'a','red',5}}}
is a set
- Lists are collections of objects that have been arranged into a specific sequence
- A collection can be sequenced on one or more attributes using .sortedBy or .sortedByDesc
- An operation can return any data type including a collection.
General structure can be
{<element> in <Collection> | <predicate>}.<operation>
when operation applies to the collection as a whole or
{<element> in <Collection> | <predicate>}.<property>.<operation>
when the operation applies to an attribute of the collection. . may be repeated arbitrarily
Sets are sometimes defined using this notation {x : x ∈ N, 3 <= x <= 4}
- this is equivalent to
{x in N where x in [3..4]}
which is an infinite set if x is real or the set {3,4} if x is integer. - this is equivalent to
{x in N where x>=3, x<=4]}
- its also equivalent to
{x in {3,4}}
when x is integer
Multiple collections can be joined and filtered. Note: In place of | you can also use "where" or "filter"
Example | Explanation |
---|---|
{color in (red,green,blue},number in {1,2,3,)} |
would generate the cross product of 9 tuples of the form <color,number>. i.e. (red,1), (red,2),(red,3),(green,1),(green,2),(green,3),(blue,1),{blue,2),(blue,3) |
{p in Persons, c in Cars where p.colorPref=c.color } |
would produce a collection of tuples <p,c> where the car has a color the person prefers |
NOTE: As you go from left to right in the expression you are getting more and more specific
Example: Generating cross products
Business Question | Possible Implementation | Return Value |
---|---|---|
Is there any person over 65? | notEmpty({p in Person where age>65}) |
returns T or F |
Are there any persons between 13 and 19? | notEmpty({p in Person where age in [13..19]}) |
returns T or F |
Are there more than 2 persons between 20 and 65? | size({p in Person where age in [20..65]}) > 2 |
returns T or F |
How many persons between 20 and 65? | n=size({p in Person where age in [20..65]}) |
sets the value of n |
Find males under 13 | mu13={p in Person where age<13, gender='male'} |
creates a collection named mu13 |
Are all the people over 65 male? | allContains({p in Person where age>65},'gender','male') |
returns T or F ?????? |
Are there any males over 65 ? | notEmpty({p in Person where age>65,gender='male'}) |
returns T or F |
Are there any non-males over 65? | notEmpty({p in Person where age>65,gender<>'male'}) |
|
What is the highest income for males over 65? | highest={p in Person where age>65,gender='male'}.income.max |
|
Is the youngest person over 65 a male? | {p in Person where age>65}.sortedBy(age).first.gender = 'male' |
returns T or F |
What is the sum of incomes for people over 65 | incomeSum={p in Person where age>65}.income.sum |
returns the sum of incomes |
Is the lowest income for over 65s greater than zero? | {p in Person where age>65}.income.min >0 |
returns T or F |
What is the average age of the oldest 3 males between 21 and 45? | avgAge={p in Person where age in [20..65],gender='male'}.sortedBy(age).first(3).age.avg |
returns a number |
Which are the oldest 3 males between 21 and 45? | oldest={p in Person where age in [20..65],gender='male'}.sortedBy(age).first(3) |
collection saved for later use |
What is the sum of the incomes for the oldest N males? | incomeSum={p in Person where gender='male'}.sortedByDesc(age).first(N).income.sum |
returns a number |
What state does the youngest person over 65 live in? | state={p in Person where age>65}.sortedBy(age).first.state |
|
Find all the males over 65. | seniorMales={p in Person where age>65,gender='male'} |
collection saved for further use |
Then find their average income. | seniorMaleAvgIncome=seniorMales.income.avg |
result is a value |
Is the highest income of the oldest 5 persons greater than 10000? | {p in Person}.sortedBy(age).first(5).sortedByDesc(income).first.income>10000 |
|
Is person with the lowest income over parm.x a parm.gender | {p in Person where income>parm.x}.sortedBy(income).first.gender=parm.gender |
|
Is there a male over 65? | {p in Person where age>65}.exists(gender='male') |
returns true or false |
or alternatively | {p in Person}.exists(gender='male',age>65) |
returns true or false |
or alternatively | {p in Person where gender='male',age>65}.size>0 |
returns true or false |
. | ||
Find the sum of the two lowest book prices |
lowest_two=Books.sortedBy(price).at(1).price+Books.sortedBy(price).at(2).price returns a number |
|
or | lowest_two=Books.sortedBy(price).first(2).price.sum |
|
or | lowest_two=Books.sortedBy(price).subSequence(1,2).price.sum |
NOTE
avgAge={p in Person where age in [20..65],gender='male'}.sortedBy(age).first(3).age.avg
can be expressed as a series of statements:
adultMales={p in Person where age in [20..65],gender='male'}
adultMalesAscendingAge=adultMales.sortedBy(age)
youngestThreeAdultMales=adultMalesAscendingAge.first(3)
avgAge=youngestThreeAdultMales.age.avg
Person.iterate() Eg {p in Person}.iterate(p.net=p.gross-p.taxes) updates p.net for all persons
size, iterator(), add() or +=, remove() or -=, clear
Operation | Example | Result |
---|---|---|
.exists | {p in Person where age>65}.exists(gender='male') |
true if any p over 65 is male |
.forall | {p in Person where age>65}.forAll(gender='male') |
true if all p over 65 are male |
.isEmpty | {p in Person where age>65}.isEmpty |
true if there are no p over 65 |
.notEmpty | {p in Person where age>65}.notEmpty |
true if there is at least one p over 65 |
.size | {p in Person where age>65}.size |
returns the number of p over 65 |
.sortedBy | {p in Person where age>65}.sortedBy(age) |
the set of p over 65 in ascending age |
.sortedByDesc | {p in Person where age>65}.sortedByDec(age) |
the set of p over 65 in descending age |
Collection must be sorted eg sortedCollection=collection.sortedBy(age)
Operation | Example | Return |
---|---|---|
.at(n) | {p in Person where age>65}.sortedBy(age).at(1) |
returns the youngest p over 65 |
.first(n) | {p in Person where age>65}.sortedBy(age).first |
returns the youngest p over 65 |
.last(n) | {p in Person where age>65}.sortedBy(age).last(2) |
returns the oldest 2 p over 65 |
.subSequence(i,j) | {p in Person}.sortedBy(age).subSequence(2,Person.size-1) |
returns all but the youngest and oldest p |
Assume A={red,green,blue} and B={red,yellow,blue,purple} where A and D are sets of instances of the same type
Operation | Example | Result | Explain |
---|---|---|---|
union | C=A+B |
C={red,green,yellow,blue,purple} | what is in A and B combined. A+B=B+A
|
union (alt) | union(A,B |
||
intersection | C=A@B |
C={red,blue} | what is common to both A and B. Note A@B=B@A
|
intersection (alt) | intersection(A,B) |
||
difference | C=A-B |
C={green} | what is in A but not B. A-B<>B-A
|
difference | C=B-A |
C={yellow,purple} | what is in B but not A. |
difference | C=(A+B)-(A@B) |
C={yellow,green,purple} | what is not in both A and B. Also C=(A-B)+(B-A)
|
difference | C=(A@B)-(A+B) |
C={} | always the empty set. |
difference (alt) | difference(A,B) |
Each should implement a common interface Common attributes (if any) must be of the same type. Common methods (if any) may have different implementations but must return the same data type
Operation | Example | Explain |
---|---|---|
union | transportation=cars+boats+planes |
merges three sets of different types into a single set with the combined attributes of all of them (effectively dynamic multiple inheritance) |
union | transportation={c in Cars where seats>4,insured=True}+(b in boats where type='hovercraft',seaworthy=True})+{p in Planes where age<5} |
merges three sets with filters |
Operation | Example | Notes |
---|---|---|
.sum | {1..100}.sum |
Sum of the first 100 integers |
.sum | {N..M}.sum |
Sum of the integers from N to M |
.sum | {n in 1..N where n.mod(2)=0}.sum |
Sum of the even numbers <= N |
.sum | {p in Person where age>65}.income.sum |
Sum of incomes of people over 65 |
.product | {1..100}.product |
Product of the first 100 numbers |
.max | {p in Person where age>65}.income.max |
Largest income of people over 65 |
.min | {p in Person where age>65}.income.min |
Smallest income of people over 65 |
.avg | {p in Person where age>65}.income.avg |
Average income of people over 65 |
.allContain() | {p in Person where age>65}.gender.allContain('male') |
equivalent to Person.forAll(age>65,gender='male')
|
.uniqueCount() | {p in Person where age in [18..65]}.gender.uniqueCount |
Number of unique genders in persons 18..65 |
[list of any valid expression involving attributes of the collection that evaluates to boolean]
{p in Person where age>65}
just those instances where age >65.
Person
is equivalent to Person[]
and Person[*]
meaning every instance of the class with no filtering.
Person[n]
any n arbitrary instances (can't think why we'd need this yet)
{p in Person where age>65,gender='male'}
allows the filters to be applied in any order (perhaps even in parallel with the final result being the intersection of the two collections.
This is equivalent to {{p in Person where age>65} where gender='male'}
which requires the age filter to be applied first and then the gender filter is applied to the results. Not sure if it makes a huge difference