The Shadow Posted December 13, 2007 Posted December 13, 2007 Since my last "request for comment" thread was so successful and productive, I thought I'd start another. For the ForEach() topic, I had a specific idea to discuss, this time, the only limitation is calculation function ideas that you think would be useful. Please try to limit the ideas to calculations though, I don't want to see a long thread about script triggers. Also, to avoid rehashing what we're already been over, let's assume some sort of iteration concept already exists in the form of ForEach() with a { Range() / Counter() / Enumerate() } helper function. If you are going to make a suggestion, please try to suggest a name, show the arguments with a brief explanation of what it does. Use [ ] to indicate optional arguments. ----------- For example, one suggested from the other thread: SortValues( , [ , [ ]] ) - a list of newline seperated values - one of: { TextType, DateType, TimeType, TimestampType, NumberType } (optional argument, default is TextType) - true is ascending (default) This function would sort the values provided in the first argument using the data type specified in the second argument in the direction specified in the third argument. Example: SortValues( "bear¶cat¶bare¶apple¶", TextType ) results in: "apple¶bare¶bear¶cat¶".
Genx Posted December 13, 2007 Posted December 13, 2007 (edited) Hate to be the first one to post but... I guess someone has to start. I want real key value arrays... I guess its kind of a calculation request: Scenario 1: Array(;[;]) Scenario 2: Array(;¶¶[;¶¶]) If the parameter is left out, the value for the key provided is returned rather than being set. In the second scenario, a pilcrow delimited list is returned. An error is thrown if a mismatched number of keys / values is provided. Multidimensional arrays should be supported - i.e. values should be able to be arrays. e.g. Weather for Monday, Tues, Wed Morning and Afternoon - Setting: Array($$weather;"Monday¶Tuesday¶Wednesday"; Array("Morning¶Afternoon";"Cold¶Sunny") & ¶ & Array("Morning¶Afternoon";"Warm¶Thunderstorm") & ¶ & Array("Morning¶Afternoon";"Heavy Rainfall¶Freezing Temperatures") ) Retrieval of weather for Monday afternoon: Array(Array($$weather;"Monday");"Afternoon") [color:orange]-- Code tags aren't working for this one for some reason. ... or: Retrieval of weather for Monday afternoon: Let(monWeather = Array($$weather;"Monday") ; Array(monWeather;"Afternoon") ) -> Returns "Sunny" Alternatively, a Set and Get method could be made available - I'm not necessarily set on the function name. Edited December 13, 2007 by Guest
fabriceN Posted December 13, 2007 Posted December 13, 2007 Here are some I can think about while getting up : * SourceTable ( table ), returns the real table name, the one you can see in the Table tab of database definition window, not one in the relationship graph. * Get ( MouseTop ), Get ( MouseLeft ), self explanatory I think. * Get ( ClickedObjectName ), returns the name of the layout object that was last clicked. * Get ( ScriptStack ), returns the names of all scripts currently running, exactly what we see at the Script Debugger bottom (some bugs are still very difficult to trap without this function) * Array ( expression1 { ; expression2 ; expression3... }) ... and some improvements to existing ones... * GetLayoutObjectAttributes ( Get ( ActiveFieldName ) should work even if the current field object is not named. Alternatively (and better, I think), all objects should be automatically named by default) * The same function could have the attribute "LoadingStatus" for the WebViewer (just a detail. * Self should work in scripts and, most of all, custom functions. * RelationInfo would be nicer... if it worked again. (the "options" line doesn't return appropriate result anymore)
comment Posted December 13, 2007 Posted December 13, 2007 (edited) Before I sail into uncharted territories, let me be a little more mundane and mention a few that are missing from the current set. One that I find blatantly omitted from the Financial functions is: InterestRate ( principal ; payment ; periods ; guess ). Two more often-needed functions are: CountIf ( field ; condition ) SumIf ( field ; condition ) (The condition parameter would be evaluated in the context of the current iteration: using values from the same related record when field is a related field, from the same repetition/s when field is a repeating field, and from the current record when field is a constant.) It would be nice to have reverse versions of the two filter functions: ExcludeCharacters ( text ; charactersToExclude ) ExcludeValues ( listOfValues ; valuesToExclude ) and of course: UniqueCharacters ( text ) UniqueValues ( listOfValues ) A few cryptographic functions such as MD5, SHA and RC4 would also be in order. To make Web Viewer a real tool instead of a layout ornament, we desperately need (a) a function to retrieve rendered text from the source HTML (similar to what you get when you choose Save Page As Text in a browser), and ( a function to access the displayed text - or any other way to get the results of a javascript. Which brings me to the last point (for now), and that is interaction with the outside world. There are endless possibilities here, but I believe the simplest way for FMI to approach this would be to provide access to the OS shell. I realize this is not exactly a function, but I mention it here because it would provide advanced developers with all the tools they can dream of, while still keeping the "native" Filemaker calculation environment friendly to non-programmers. Edited December 13, 2007 by Guest added CountIf and SumIf
fabriceN Posted December 14, 2007 Posted December 14, 2007 Yes, I agree with Comment's ideas. Even if some of them (countif, sumif, excludeValues, UniqueValues) can be done with ForEach.
The Shadow Posted December 15, 2007 Author Posted December 15, 2007 Array(;[;]) Array(;¶¶[;¶¶]) If the parameter is left out, the value for the key provided is returned rather than being set.... I like the associative array idea, but that's some pretty ugly / weird syntax in my opinion. How about if they were more like filemaker's existing variables? I see two possible angles to go with here: - Rather than $var and $$var, they would look like %var and %%var: Let( [ %weather["Monday"] = "Sunny"; %weather["Tuesday"]="Windy" ]; ... ) - Or, put them in the same namespace as the existing variables, but add a different index syntax to indicate string indexing: Let( [ $weather{"Monday"}="Sunny"; $weather{"Tuesday"}="Windy"; ... ) So, for the % syntax, they would be in a different namespace, but would work much like the existing variables do, but indexed via strings. For the $ syntax, I think they would be in the same namespace, so: $var[1] would be the same value as $var{"1"}. Also, the variables could be changed so that multiple indexes were permitted like this: Let( $var[1][2] = 12; $var[2][0] = 15; ... ) or with strings: Let( $var{"a"}{"c"} = 12; $var{"d"}{"m"} = 15; ... )
The Shadow Posted December 15, 2007 Author Posted December 15, 2007 * Get ( MouseTop ), Get ( MouseLeft ), self explanatory I think. A minor nit - This should be one function that returns the coordinates, otherwise by the time you get the other coordinate the mouse may have moved and you wouldn't be certain to have a "real" place the user was ever pointing to. * Get ( ScriptStack ), returns the names of all scripts currently running, exactly what we see at the Script Debugger bottom (some bugs are still very difficult to trap without this function) Neat idea. * Array ( expression1 { ; expression2 ; expression3... }) Uhm, but what does it do? * Self should work in scripts and, most of all, custom functions. What do you expect from within a custom function? The field that the custom function is solving to? What about for scripts, the field for a SetField? What about other steps?
The Shadow Posted December 15, 2007 Author Posted December 15, 2007 CountIf ( field ; condition ) SumIf ( field ; condition ) (The condition parameter would be evaluated in the context of the current iteration: using values from the same related record when field is a related field, from the same repetition/s when field is a repeating field, and from the current record when field is a constant.) So, I have a table A with x,y,z fields in it, then a call might look like: SumIf( A::x, A::y+A::z > B::m ) where y and z would be from the current A::x record, and B::m would be the first B record related to the A record being summed? Pretty good idea, I think. If they were going to bother doing it, might as well as add ListIf(), AverageIf(), etc. Once the basically functionality is there, the others are all cheap to add. This function, unlike the normal aggregators (Sum(), List(), etc) could be defined to work with empties, so you could count empties in a repeating field with: CountIf( rpt; IsEmpty(rpt) ) I don't get what you mean by a constant above, and speaking of that, what do you guys have against examples?? Which brings me to the last point (for now), and that is interaction with the outside world. Personally, I think this is the sort of thing with enough options and choices where is belongs as a ScriptMaker step rather than inside calculations. Something like "Execute In Shell" with options for: Whether to wait, how long to wait, where to store the result, what program do you want to pass your input to, etc. etc. So, great idea, but not for calculations (IMO).
comment Posted December 15, 2007 Posted December 15, 2007 I don't get what you mean by a constant above, and speaking of that, what do you guys have against examples By constant I mean a specific number or a reference to a local field. The problem here is that the current aggregate functions have several possible behaviors, according to the type of reference, and whether there are repeating fields involved. It would take a matrix to show all the variants, but basically there are two syntaxes: Sum ( related::Field ) sums the values of Field in the entire related set; Sum ( related::Field ; 10 ) adds 10 to the value of Field in the first related record. It's almost as if there were two separate functions, one for a single parameter, another for multiple parameters. It is difficult to add another parameter for condition and still maintain consistency with the existing behavior. Perhaps optional [] brackets need to be used, so: • SumIf ( [ Child::Field ] ; Child::Status ) - sums those values of Field in related records where Status = true; • SumIf ( [ Child::Field ] ; Status ) - sums the values of Field in all related records, but only if parent Status is true, otherwise returns 0; • SumIf ( [ Child::Field ] ; Sibling::Status ) - sums those values of Field in related child records where the status of first related sibling record (from the context of Child) is true (this agrees with what you suggested); • SumIf ( [ RepeatingField ] ; RepeatingStatus ) - sums those repetitions of RepeatingField where a corresponding repetition of RepeatingStatus is true; • SumIf ( [ 1 ; 2 ; 3 ] ; Status ) - returns 6 if Status is true (in local record), otherwise returns 0; • SumIf ( [ 1 ; 2 ; 3 ] ; Child::Status ) - returns 6 if the first related child record's Status is true, otherwise returns 0. Of course, if it were up to me, I would split the existing Sum() function into two, Sum() and SumArguments(). The first would be allowed only a single parameter, and if the supplied parameter was a reference to a local field, it would summarize the found set (similar to GetNthRecord). The second function is not truly an aggregate function and isn't really all that useful, but it would be needed for backwards compatibility.
comment Posted December 15, 2007 Posted December 15, 2007 So, great idea, but not for calculations (IMO). I am of two minds on this myself. On one hand, it would be nice to have a wait parameter. OTOH, if not abused, it could be very well used inside a calc, e.g. Let ( array = DoShellScript ( myCreateArrayScript ) ; x = MiddleWords ( GetValue ( array ; 2 ) ; 3 ; 1 ) ; ... I am not sure what you mean by "where to store the result, what program do you want to pass your input to, etc. etc.". I was thinking of something simple, where the script is passed as text to the function, and stdout is returned. Everything else needs to be defined in the shell script itself. If, during the course of the script, you want to call other applications, write to a file, or erase your entire hard disk - that's no concern of the function.
Genx Posted December 15, 2007 Posted December 15, 2007 Hey if arrays could be dealt with in that way, I would much prefer it to an actual function... I just thought we had to provide functions (sorry). Anyway, I'm not sure the additional name space would be wise - after all, while they are arrays, they are still the same as other "repeating" variables in every way other than the fact that the keys are strings rather than ints. So yes, if Let($var{"key"} = "value"; $var{"key"} ) would return "value" that would be straight forward. Additionally, you should be able to use arrays with standard let based variables (i'm not sure what the technical name for them is) e.g: Let(var{"key"} = "value" ; var{"key"} ) I'm not sure how the additional name space would work out here.
comment Posted December 15, 2007 Posted December 15, 2007 Continuing with more missing basic functions: Aggregate: • Median() • Mode() • Product() • Rank() • Percentile() • WeightedAverage() • GCD() • RootMeanSquare() Text: • Unicode ( character ) - returns the numeric value of the character's Unicode code point; • Character ( codePoint ) - reverse of the above. • FormatNumber ( number ; format ) • RepeatText ( text ; numberOfTimes ) Dare I mention RegEx? Probably an overkill in the current state of things, but at least I would like to see a better TrimAll() function - one that would work with all white-space characters and remove all non-printing characters. I believe these are all self-explanatory (mostly stolen from Excel) but if not, I will provide more detailed explanations (with examples! ) where needed.
The Shadow Posted December 15, 2007 Author Posted December 15, 2007 It's almost as if there were two separate functions, one for a single parameter, another for multiple parameters. ... • SumIf ( [ 1 ; 2 ; 3 ] ; Status ) - returns 6 if Status is true (in local record), otherwise returns 0; .... Of course, if it were up to me, I would split the existing Sum() function into two, Sum() and SumArguments(). This second form doesn't seem very useful to me, and I also dislike the split-personality of normal Sum(). I don't see any reason that weirdness needs to infect SumIf(), etc. too. The original Sum(), etc. could be broken into two functions, but that would have to wait until a file-format change as it would require all existing calculations to be converted. I think SumIf(), ListIf(), etc. are already more complex to explain (like in the help file) and understand than their unconditional counterparts - adding the two modes would just make it worse.
The Shadow Posted December 15, 2007 Author Posted December 15, 2007 I am not sure what you mean by "where to store the result, what program do you want to pass your input to, etc. etc.". I was thinking of something simple, where the script is passed as text to the function, and stdout is returned. Everything else needs to be defined in the shell script itself. If, during the course of the script, you want to call other applications, write to a file, or erase your entire hard disk - that's no concern of the function. I guess I'm thinking more of how I'd do it on unix, if you wanted to avoid writing a file every time you hit the step you could just pipe the text to the right program (#! /usr/bin/perl for me) and have it execute it and get the result back. There has to at least be arguments to be able to pass to the thing, or some type of escape syntax to be able to insert fields from your current context into the shell script. I'm thinking I want to *see* the script, not some huge mess of: "#!/bin/sh¶ " & "echo "Hello!"¶" & "/bin/rm -f /tmp/a.pdf"
fabriceN Posted December 15, 2007 Posted December 15, 2007 Oh yes ! Comment, Unicode() and the reverse : input a code (Unicode or ASCII) and result as string. Median is also one I miss. Also, I uploaded a feature request a few weeks ago, and as it is "boder line" with calculations, I would like to know what you think of it. The idea was to have a script step, that would turn on/off auto-enter options, as the check box we have after setting up an import order does (the auto-enter options can be suspended during import) Additionally (but this is secondary), a check box at the field auto-enter option tab level could make this field insensitive to this "variable" auto-enters would execute in any case. As far as my first wish is concerned ( SourceTable ( table )), I found this morning (only !) this very interesting file by Alexander Zueiv, and I'm apparently not the only one to miss this feature. http://web.mac.com/zueiv/iWeb/FileMaker/Script%20Killing%20Techniques/AFA363C6-2D85-4C6D-82E2-273F3722A69E.html As to Self. I really regret the way this function was implemented. I had hoped it would return the field name (then it would be easy to get the contents), and it would allow all formatting functions such as FormatAsAPhoneNumber() to be without parameter if it worked in custom functions. I sometimes keep using fields comments (yes !) to be able to calculate something out of the database structure...
comment Posted December 15, 2007 Posted December 15, 2007 I don't see any reason that weirdness needs to infect SumIf(), etc. too. The only valid concern here is consistency. But if there were a strategic decision to make the current functions consistent with the new form at the earliest opportunity, that should be enough to satisfy the purists, I think. I'm thinking I want to *see* the script, not some huge mess I am modest, and I would rather have that than nothing. Besides, there are ways to avoid the "huge mess", such as putting the script in a text field, at least during testing. Then Filemaker can produce the escape syntax for you, using the Quote() function (and reverse the effect with Evaluate()). We have much the same situation now with the Web Viewer, and it's working reasonably well. Using single quotes instead of double also helps a lot - and for anything really complex you could put the real script in an external file and call it with a simple one. I would really like to see this usable within a calculation. For FM scripting, Mac users already have a good solution in the form of Applescript (that can in turn call shell scripts). If FMI would only put VBScript on the same footing, i.e. get the result - I believe that should be enough for anybody, on that front. But when I think of all the calc functions I could get my hands on by calling a shell...
The Shadow Posted December 16, 2007 Author Posted December 16, 2007 Hey if arrays could be dealt with in that way, I would much prefer it to an actual function... I just thought we had to provide functions (sorry). No need to be sorry. I just meant to limit the discussion to calculations rather than all FM features, not only calculations functions. Btw, internally these are functions too, they just don't have normal "names"; when the parser sees: $var[x+y] it converts it for internal use into something like: GetVar( "$var"; x+y ) With a corresponding SetVar(name,index,value) function for when it is seen inside a Let() declaration. So yes, if Let($var{"key"} = "value"; $var{"key"} ) would return "value" that would be straight forward. Okay, let's continue with this path then. I think, particularly with string indexing, there *must* be a way to get them all, how about something like: Indexes( or ) that would produce a list of all indexes that held non-empty values (and so, are defined). Let( [$var[2]=1; $var[5]=11; $var{"Hi"}=7]; Indexes($var) ) might produce output like: "5¶Hi¶2¶" When used with a repeating field, it would similarly return a list of the indexes of all non-empty reps. When used with multiple indexes, it could return the indexes of the next level down: Let( [$var[1][2]=1; $var[2][5]=7; $var[1][3]]; Indexes( $var[1] )) would return "2¶3¶". Additionally, you should be able to use arrays with standard let based variables (i'm not sure what the technical name for them is) e.g: Let(var{"key"} = "value" ; var{"key"} ) I call the three types: local, script-local, and global. The issue here is backward compatibility, local variables were introduced without reps in FM 7, which is why they didn't suddenly obtain reps when script variables were added. Despite being usable in the same spots, they are implemented in completely different ways. So, to make a change to them would basically require waiting for a file-format change, otherwise calculations that looked like they should be compatible with 7->9 wouldn't be, as the internal storage would differ if there was "room" for a repetition there.
Ocean West Posted December 16, 2007 Posted December 16, 2007 FoundList( field ; unique ; sort ) This would provide a return separated list for the current found set of records, field: a local field (text/number) unique: default 0 or false, or returning all records. 1 or true would return unique values sort: default 0 current sort order, 1 ascending, 2 descending, "valuelist" custom based on value list either a return separated list or the name of a value list using:ValueListItems ( Get(fileName) ; valueList ) The sort would be optional based upon the field type of source field number or text. This would evaluate only for "local fields" not related fields. To include multiple fields in the value use of List ( field {; field...} ) could be used as the source field? - haven't though it fully thru.
The Shadow Posted December 17, 2007 Author Posted December 17, 2007 FoundList( field ; unique ; sort ) I think this function is trying to do too much at once, if there were functions UniqueValues(), and SortValues(), then *this* function could concentrate on obtaining the found-set data (in the found set's current order always). Having the functions split up is generally a good idea if you're ever going to need the sorting / uniquing abilities elsewhere. I personally like putting the verb first, so I think I would call this function: ListFoundSet( ) which would put it nearer to List() which it is similar to, and clarify "Found" to be the noun via "FoundSet". One way to include multiple fields would be to say that if the argument to the function was not a field, it is an expression to evaluate for each record in the found set and return their result, for example, with a table with x,y fields: ListFoundSet( x & ": " & Left( y,1 ) ) would walk over the found set performing the action for each record, and combining the results into a return separated list. Then, this calculation could include related references that were evaluated from each of those records (like the SumIf() that was discussed earlier). Another valuable idea I liked was the suggestion of sorting by a value-list. I think that would be most flexible as an extension to the earlier SortValues function, where the value-list would be placed in the data-type argument: SortValues( ; ValueListItems( Get(fileName); ) )
The Shadow Posted December 17, 2007 Author Posted December 17, 2007 Let($var{"key"} = "value"; $var{"key"} ) Another issue came to mind about this, when using the text-indexed variables it is probably not going to be possible to support the full 2 Gig text limit for the index (supporting it would lead too inefficiencies for the more normal cases). I think saying only the first 1000 characters of the key will be used for indexing would be fine for most uses.
The Shadow Posted December 17, 2007 Author Posted December 17, 2007 Aggregate: • Median() • Mode() • Product() • Rank() • Percentile() • WeightedAverage() • GCD() • RootMeanSquare() This makes me wonder if there needs to be some more general aggregation method that would handle the walking over related records part and give the functional control back to the user (like ListFoundSet()). I don't see a good general way to provide something like that for the above list though, each has its own data requirements (Mode() could potentially store all values seen while processing, I think).
The Shadow Posted December 17, 2007 Author Posted December 17, 2007 One way to include multiple fields would be to say that if the argument to the function was not a field, it is an expression to evaluate for each record in the found set and return their result, for example, with a table with x,y fields: ListFoundSet( x & ": " & Left( y,1 ) ) This functionality seems like it would be useful with related records also, maybe, breaking my own no "ForEach" rule : ForEachRelated( ; ) to collect up a set of computed result for each related record in , where would be evaluated in each related record's context in turn. could be this table occurrence to walk over all records in this table (ignoring found set), and: ForEachFound( ; ) would walk over this found set, and access all found records related records in turn. Again, using this table as would just walk this found set. Hmm, these might be a little tricky to explain in the help, and if you started nesting them if would get confusing about which context a expression was being evaluated from...
comment Posted December 17, 2007 Posted December 17, 2007 (edited) If all aggregate functions were stripped of their "dual personality", they should all summarize the found set if the referenced field is a local field - including the List() function. I do however like the idea of replacing the reference with an expression. But such behavior would have to be thought out carefully, because an expression like: List ( Child::Name & Sibling::Name & Grandchild::Name ) is ambiguous regarding which TO provides the basis for the "walk-through". I don't quite see the value of sorting by a value list. A value list is always sorted ascending, unless it's a custom value list, and IIRC values not included in the list are excluded from the sort. I agree that sorting is best left to a separate functions, but there could be one advantage to including it in List() - and possibly in GetNthRecord() too: once you have the values isolated, the sort order is by necessity dictated by the values alone. With List(), values could be sorted by another field in the same record or even better, by an expression. --- EDIT: This was posted before I saw your last two posts. Edited December 17, 2007 by Guest
Vaughan Posted December 17, 2007 Posted December 17, 2007 "I don't quite see the value of sorting by a value list. A value list is always sorted ascending, unless it's a custom value list, and IIRC values not included in the list are excluded from the sort. " Not sure what you mean by "excluded from the sort"... IIRC the un-listed values are sorted at the end, after the others, in alphabetical order.
comment Posted December 17, 2007 Posted December 17, 2007 I meant they are excluded from the sort by value list - just as you say. I couldn't remember if they are listed alphabetically or unsorted, but the important thing is that they are listed separately.
The Shadow Posted December 30, 2007 Author Posted December 30, 2007 After having thought about the iteration over records more, I think maybe one function is all that is needed: ForEachRecord( ; ) So, if is a local field, this will move over each record in the found set that has a non-empty value for that field (walking over the table's primary key would give all values for the found set). If is a related field, it would iterate over those related records that had a non-empty value for that field. Either way, the expression can be whatever is needed, and the results are all concatenated together as in ForEach(). This provides the generic related record walking needed to implement any of the aggregate functions that are desired.
comment Posted December 30, 2007 Posted December 30, 2007 Can you explain how it will aggregate? For example, what will 'expression' be to get the sum of values?
fabriceN Posted December 30, 2007 Posted December 30, 2007 I guess Sum ( Substitute ( ForEachRecord ( field ; field ) ; ¶ ; ";" ) But are you saying this function would replace ForEach() ?
comment Posted December 30, 2007 Posted December 30, 2007 I guess Sum ( Substitute ( ForEachRecord ( field ; field ) ; ¶ ; ";" ) That wouldn't work, at least not with the current Sum() function, because after performing Substitute() you'd be left with: Sum ( "a single text string containing some values separated by semicolons" ) which produces an evaluation error. You could of course use Evaluate() on top of that, but that would be a very minor improvement on what's already possible today using the List() function.
fabriceN Posted December 30, 2007 Posted December 30, 2007 (edited) Absolutely. This is why I am also concerned with the idea of killing ForEach in the egg (is that an English idiom ?) EDIT re-read my first post. It was not clear at all, sorry. Edited December 30, 2007 by Guest
comment Posted December 30, 2007 Posted December 30, 2007 I'll let Shawn speak for himself but I understood this as being in addition to ForEach(), not instead.
The Shadow Posted December 30, 2007 Author Posted December 30, 2007 But are you saying this function would replace ForEach() ? No, no, as an additional ForEach option. Normal ForEach() will work on a list of values, but it doesn't help *obtain* values from related records or the Found Set (as Ocean's West's original suggestion mentioned).
comment Posted December 30, 2007 Posted December 30, 2007 If it's just for getting the values as a list, List() already does that for related records. I wish it would work the same for found set, but ForEach() could do it too: ForEach ( n = Range ( 1 ; Get(FoundCount) ) ; GetNthRecord ( field ; n ) & ¶ ) Of course, it's also possible to include some processing in the expression part of ForEach(). But doing real aggregation this way (e.g. average) would be rather awkward.
The Shadow Posted December 30, 2007 Author Posted December 30, 2007 Can you explain how it will aggregate? For example, what will 'expression' be to get the sum of values? I didn't mean to imply it would be easy or convienent, just that it would be possible. Like ForEach(), this function would just append the result for each record, which wouldn't be exactly what was needed for summing. So, as was suggested, for simple cases the values could just be prepped for an evaluate: Evaluate( ForEachRecord( A::x; A::x & "+" ) & "0" ) or a variable could be used: Let( [$mySum=0; $junk = ForEachRecord( A::x; Let( $mySum=$mySum+A::x; "" ))]; $mySum ) in either case, it would be trivial to extend these methods to perform a SumIf(), CountIf(), etc. To perform Mode(), an associative-indexed variable could be used to store all values seen, with their values equal to the count seen. [That's not to say that Mode() or CountIf() don't deserve to be built-ins, just that ForEachRecord() is a powerful addition for aggregating things.]
The Shadow Posted December 30, 2007 Author Posted December 30, 2007 Of course, it's also possible to include some processing in the expression part of ForEach(). But that is the point, the expression is occurring from the point of view of the related record you're moving over. If I have a school database, I'm on a Teacher table record, and have relationships to the Student table (the student's this teacher teaches), and the Student table has a relationship to a Parent table, then I can count the number of orphans being taught by this teacher with: Evaluate( ForEachRecord( Student::id; If (Count(Parent::id)==0;"+1"))) This would be tricky without this function, you would probably have to create an additional relationship just for this purpose.
Recommended Posts
This topic is 5680 days old. Please don't post here. Open a new topic instead.
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now