Jump to content


  • Content Count

  • Joined

  • Last visited

Community Reputation

0 Neutral

About qube99

  • Rank

Profile Information

  • Gender
  • Location
    Dripping Springs, Texas

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

  1. There are times when you will have a range of values in a field across a found set of records that you would like to scale to another range. Common ranges to scale are 0-1, 1-10, 1-100 etc. The input values could be anything of any range. To do this start by creating 4 global fields called oldMin, oldMax, newMin and newMax. oldMin and oldMax are the smallest and largest values in your found set. newMin and newMax are the smallest and largest values you scale the range to. Go ahead and populate the newMin and newMax values. Let's use 1 and 100 for example. To populate oldMin and oldMax I simply sort the records ascending by myValue field, go to first record and get the value for oldMin, then go to the last record and get the value for oldMax. We now have all 4 global fields populated. Create a calculation field, let's call it Ranking. Use this formula: ((myValue - oldMin) / (oldMax - oldMin) * (newMax - newMin)) + newMin All the values will now be ranked 1-100 in the Ranking field. To make this dynamic so that it changes as new records alter your oldMin and oldMax, just add the sort, first record, last record routine to the end of your create new record script after myValue has been populated. This works with found sets so there's some versatility there. You are also not restricted to any predefined range for the scaling. Just set newMin and newMax to be whatever you'd like for your scale. The attached image shows myValue scaled to Ranking (1-100)
  2. It looks to me like he is already using portals on a tabbed layout. So let's think about another way to create a list of all the tasks. You have 6 task tables that are related to a single record. Those 6 tables are displayed in portals. Maybe a special table could simply be related to the other 9 so that when a new record was created somewhere, a copy is created in the special table with a little scripting. In this manner he can have the single table schema for reports, subsummaries, charts, etc without altering the multi-table scheme he is probably employing. Seeing those portals tells me his schema is more sophisticated than I originally thought. Can we get a peek at your ERD? If I were designing this from scratch I'd use a single task table for the whole project. It would have the same layout as now but would link to the table with the various methods LaRetta describes. It would not matter if that table had 150 or 200 or 500 fields in it since you will only display a few of them at a time.
  3. I think I might have bult only a single task table and designated A, B and C in a field, like a category. That makes it pretty simple to view them collectively or individually. I might go further and do the same for Projects so that there is a single table for everything. Then use various layouts with filtered portals to display just the parts I want to see. A data structure like this will also open up your possibilities for creating subsummaries. You might enjoy the FileMaker Training Series. I think its a free download (sorry dont have the link) and is pretty good for learning these sorts of things.
  4. I have an update on this. While Wim's evaluate() method would work beautifully in some circumstances, and I recommend it be the first thing you try, it would not work in my special case. I now have workaround. Instead of writing an expanding list of conditions into a variable for the if() to test using evaluate(), I wrote them to a table and linked it through a multivariate relationship. When my filter function runs it now loops through those portal records and tests them 1x1, exiting the loop on the first test that fails. This certainly works better than scripted finds and yielded a nice improvement in speed for this elaborate calculation. It is still not optimal because of all the disk I/O. What I need to think about now is a way to build that list in a variable that I can loop through and eliminate the disk I/O.
  5. You might set a global variable containing the record ID while you are on you record of interest. When you get ready to go back use that global variable which contains your record ID. There's a couple of other ways to approach this.
  6. I could not get it to work in a production environment. You cannot use that if() wth a variable that contains evaluate() anywhere else except right where you're going to use it. It gives very wrong results otherwise. You cannot even wrap that variable in an if() without it breaking. Also, you cannot use the variable in another script and cannot write to it from another script. It only works in very limited circumstances. One might guess that evaluate() is working in the context of where it was created, not in the context of where you want to instantiate it. The thing simply won't work where I need it to. I can get it to work properly but then I can't write to it. Or I can write to it but it won't work. Just no way to do both. I don't know if case() will behave any differently. Probably have the same trouble with any function loaded into a variable. Any other ways of doing a branching logic test?
  7. Thanks, Wim. You get my upvote. I now have a dynamic if() using evaluate(). I started with an if() with 2 boolean tests if[ $$parentLastMove ≠ "AL" and $$recentAncestors_AR ≠ $$ancestorLimit_AR] I replaced the if() to read if[evaluate($$filter_AR) where $$filter_AR contains the same thing the original if() had $$parentLastMove ≠ "AL" and $$recentAncestors_AR ≠ $$ancestorLimit_AR I made a copy of the enumerator and ran both versions on a short problem. They both reported the exact same results. Had evaluate() not been correctly evaluating both of the boolean tests, I would have seen more nodes and more duplicates in the results. Deleting the second condition and testing proves this. So I now add the dynamic part and run a larger problem that had already been run. The results were again identical except now $$filter_AR contained $$parentLastMove ≠ "AL" and $$recentAncestors_AR ≠ $$ancestorLimit_AR and $$Ancestors_5 ≠ "BR AR BL AL BR" and $$Ancestors_6 ≠ "AL BL AR BR AL BL" and $$Ancestors_6 ≠ "BR AR AR BR BR AR" and $$Ancestors_6 ≠ "BL AL AL BL BL AR" and $$Ancestors_6 ≠ "BL BL AR AR BR BR" and $$Ancestors_6 ≠ "BL BL AR BR AL BL" All of the extra terms were added dynamically on the fly and functioned properly. I logged the processing time and evaluate() has very little overhead. It eliminates countless millions of scripted finds for me which have very large overhead. The net gain is quite significant and the cost to implement it only took a few minutes. This technique avoids disk I/O and takes up very little memory.
  8. I think the answer might be on page 9-23 of FTS 12 - multi-line keys. A portal will match a record that contains a line delimited list in the match field. My loop has 2 functions in it, a filter and a find. When find sees something it wants filter to know about, it can write it in a multi-line key field, a single global field in a related table. That field will contain a constantly changing list of things to filter while the loop is running. Then when the filter runs it tests that global field for the if() condition and should return true if anything in the list matches. There's no way to revise the contents of the if() on the fly, can't even do it with the script paused, but we can revise the contents of fields on the fly. It turns out that the portal has the proper function needed for this, the ability to match any item in a line delimited list. The end result is a dynamic filter using if(). I'll code it tomorrow and let you know. I'll aslo see whether if() can match a line delimited variable without using a portal and related table. It's a pretty cool problem, something that most folks would never have a need for. I'm constructing a ternary tree database. The algorithm that creates the records spawns duplicates. Each duplicate then spawns 3 duplicate children, and so forth. One of the problems I am running requires a tree with 3,628,798 nodes. But there's only 40,320 unique nodes, all the rest are duplicates. This means it is essential to if() those suckers outta there before they enter the main processing part of the loop and start spawning vast numbers of duplicate children. My first 2 filter conditions cut the nodes in half. But the higher order duplicates are far beyond my math ability to predict. So I catch a duplicate with find and then terminate it's family tree in filter. Each new level of the tree spawns all new duplicates. The next problem I'll run has 479 million nodes and 3.6 million uniques meaning there are going to be about 476 million duplicates to weed out. And believe me, I am not going to do 479 million scripted finds on 479 million records. No way. I'd rather filter 'em out before they start breeding. This sort of filtering can be effective. A smaller problem had a 3.6 million node tree. I found the 70 unique members after processing only 18,000 nodes. It was partially filtered which killed off 5,280 parent nodes before they could spawn duplicate children. It will get faster with full dynamic filtering.
  9. I have an if() that contains a list of conditions separated by logical AND. Can I revise that list of if() conditions on the fly? When another part of my program detects something I'd like to add another condition to my if() so that it's there when the program loops back through. I just can't think of a way to do that.
  10. I recently tested a few script methods for speed and thought I'd share what I saw. I have a FMP project that constructs a ternary tree database. I set up a design and ran it under varying circumstances in order to determine my best practices for this project. I am learning FMP and would like to know these things for all my projects. As a baseline I ran 2000 nodes of a single design. I looked at 3 main script parts. Times are in milliseconds No filter, no find, no print 1569, 1611, 1580 No filter, no find, with print 5446, 5512, 5473 about 4 seconds to print 2000 records 1x1. No filter, with find, with print 31970, 31940, 31936 That scripted find is a monster. Let's try to filter things. The data has to pass 2 tests. Filter A is a single if with 2 conditions in it and filter B is 2 nested if's for the same tests. With filter A, with find, with print 31616, 31609, 31638 With filter B, with find, with print 31648, 31680, 31667 It's a bit faster to use a single IF with logical AND for the 2 tests. And I wonder what the overhead for a frozen screen refresh is 0 Refresh 31648, 31676, 31667 1 Refresh 32607, 31895, 31862 Insignificant for my project. I can refresh as much as I'd like. Next I want to examine that scripted find and see what happens as the number of records increases. I'm also looking for a way to get field validation to return a boolean so I can see if that's any faster. Another avenue to look at is to print all the records and post-process duplicate removal. Maybe that's faster than using a scripted find.
  11. I was unable to figure out how to use validation in my scenario. I need a boolean result for branching logic and I was unable to get validation to return anything other than 0 for get(lastError). My script goes haywire with it so I need a way to control the thing. Maybe someone has a method they can share. FMP is really not going to succeed at anything other than the smallest problems I'm working on. It has proved invaluable as a rapid development environment. I'll recode the thing in C++ once I get all the parts figured out. I have done a bit of script method efficiency testing so I'll start a new thread for that.
  12. I wasn't speaking generally but about my specific project. I looked at all the global variables in it (for the umpteenth time) and I only see one place where a script parameter could be used. In all other circumstances I cannot imagine that a parameter could be used without constructing some Rube Goldberg methods of parsing them, say passing 25 variables used in multiple subscripts in a single parameter. This specific application is pretty unique and not applicable to normal business logic. What's of utmost importance at this stage of development is speed. Some of the calculations I will attempt with this system are truly immense. Every millisecond counts a great deal. One problem I dream of solving some day involves examining 6.4 quadrillion nodes to gather statistics on the 21 trillion unique states contained in them. That'll probably require a C++ application (or a quantum computer) as I doubt that FMP can ever run fast enough. But I do want to use FMP for the largest problems it can reasonably solve. What's reasonable? Good question. Joseph Becker ran a similar C++ enumerator for 7 months non-stop to find the answer to this combinatoric question: http://smallpuzzlecollection.blogspot.com/2011/12/one-in-trillion.html And risky. There was no guarantee that Becker would find any solutions at all! I am now working mostly on user features (grunt work) and starting to think about optimization. I certainly don't know enough about FMP and am just learning. One thing I could sure use is a way of measuring time in milliseconds to avoid conducting tests that take too long. For instance, can a number be passed faster as a variable or as a parameter? How about passing text? Which is faster, Set Field, Insert Calculated Result or Replace Field Contents? Can a conditional run faster as a text string or would it run faster as Unicode? Same for scripted finds, what's the fastest way? Most optimization will be achieved in the logic. For instance, I don't want to waste time creating duplicates so I run a test. That gave a big boost. But there's a LOT of duplicates and it's best if they never get processed then eliminated in the duplicate test script. For instance, I noticed that when a parent spawns an anti-child it is creating a duplicate. The child is it's own grandparent. So I wrote a filter to not even look at an anti-child. That gave another big speed boost. I also see that spawning more than S/2 identical members in a row creates a duplicate of an anti-member. Visualize turning a dial past halfway is the same as turning the dial the other way a lesser amount. I haven't implemented this filter yet but it's quite certain to offer another big speed boost. Can I ever eliminate all sources of duplicates so that the scripted find can also be eliminated? Probably not. As the levels increase the combinatorics of duplicates also increase. Maybe there's a predictable expanding series that I can recognize. I will try to solve the filters one by one and see. If I can prefilter all duplicates then there's no need to create records at all. They are only there so that duplicates can be found. It's the statistics that really count, not individual records.That'd be a pretty big speed boost. In order to determine the best script practices for my particular project I'd like to learn... How to measure elapsed time in milliseconds and How to show progress while the main window is frozen Once I can measure time in milliseconds I can test functions and data types (without running tens of thousands of iterations). The results might be applicable across a broad spectrum of projects. I'll document what I find in these tests. I realize that saving a few milliseconds here and there is utterly meaningless to normal FMP projects. For me it will mean saving days and weeks of computing time.
  13. Looks like you;'ve given this a great deal of thought. I had run into the limits of human thought but never stated it so clearly.A major part of solving a problem is to state it clearly, which you have done. I don't know how long these scripts in my current project would have taken to write if I had not broken the thing into subscripts. I tried but it was mentally overwhelming. I was forced to break the problem into smaller peices and deal with each in turn. I got it working properly and am now cleaning up the scripts. It gets a little messy when ya do countless script revisions over many days. I have a current project that uses a number of global variables. My thinking is a variable that will be used millions (even billions) of times in a calculation is best created available in memory rather than trying to scope it and have it created and calculated over and over again. I also wanted to avoid all the disk I/O calls possible so I used global variables instead of fields. This approach required me to spend a long afternoon tracing every single variable to make sure it was properly dealt with. I wanted to ensure that I wasn't wasting resources with orphaned variables that can occur in a heavily revised project. There's nothing quite like tracking down a memory leak in a delivered project. Much better to deal with it while in development. Same goes for fields where data is appended. One thing I did to help was to comment most of the variables in addition to descriptive names and tell which subscript would use it and which script it came from. Very helpful for low storage minds like mine. Yeah it took time to do that but I will probably return to this project perhaps in a few years so I need to use the system to remember things for me. The subscripts were useful in another way. They could be toggled on and off from the master script. This allowed me to use multiple variations of a subscript I could quickly swap. Funny how a script that works fine will suddenly go insane when another script operating on the same data is added. After I was satisfied the extras were simply deleted from the script directory. I may redo the calculation part of this project in C++ but I must say that employing FMP made it all dramatically easier to develop. I would go so far as to call FMP a rapid development environment for this sort of work. I used FMP's excellent display functions to show me what was happening. Impossible to do that in an ANSI/ISO console application without coding some sort of interface that poorly duplicates what FMP already does so well.
  14. Thanks for the tips. That file was in Dropbox website. It's just a temp file. I started off looking at my data structure as a binary tree with no knowledge how to code one. After seeing this illustration at Wolfram the solution came to me. First I realized that my data structure was actually a ternary tree, exactly like this: Looking at this I realized that I could construct one with FMP without using recursion. Here's how. The nodes are numbered sequentially. So all I had to do was use a node counter. Go to node 1 (which is record 1 in FMP speak) and spawn 3 children with a script. Increment the node counter by 1 and go to node 2 and run the 3 child spawn script again. Each child becomes a node for more 3 child spawns, and this could go on forever. So I used a loop to keep going until the termination conditions are met. In my case most nodes are duplicates and these were not written to the data (using a conditional) and therefore did not become parents for spawning vast numbers of triplicate children. If I were to graph my ternary tree it would be pretty ragged looking with lots of terminal nodes called "leaves". Every record is connected back to the root through its parents, grandparents, etc and this graph shows exactly those relationships. My next challenge is efficiency and speed. My first full calculation with this will have a bit ovber 40,000 nodes and that's the smakllest problem. Some larger problems I want to run will have many millions of nodes. I think I have a handle on memory bt I'll look for ways to cache the data to reduce disk I/O. I also have a scripted search in each loop that coulkd be a real time killer. Might be a few places where I can reduce redundant script calls. Now for some practice attaching a file. Enumerator 0.03.zip
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.