Jump to content

Comparing software text versions


This topic is 3547 days old. Please don't post here. Open a new topic instead.

Recommended Posts

I need to compare three text strings which hold software versions. The checkVersion field must be >= the minimumVersion field and < the currentVersion field and if so, script will continue with an update.

 

The patterns

There is consistency in entering the strings as ##0.###.### ### but some managers pre-pad with zeros and some don't. And some use 'build' and some use 'v' or 'ver' so running comparisons is difficult.

 

What I need to solve

At this point, I want to just solve it via script and not deal with the hard-coded versions within each of the various files. Please see attached fp7 file for examples of each type of possible combination.  Neither do I want to create relationships at this point.  Rather I would like to focus on ways of comparing these strings.

 

Data-entry validations not needed

The strings are not entered into fields at present so I do not need to validate the pattern. If testing, just be sure to follow same logic as the examples.

 

My solution:

I turned all the versions into a number for comparison.  It seems to hold up under testing.  The calculations are for easy viewing of results and  won't be needed – the logic will be ran in a script when version checking.  So I succeeded in creating the 'consistent number' using a custom function in the attached fp7 and I can check whether the text string is 'within the range' of versions but I sense slop in my number creation approach.  

 

I always wonder if there are better ways from which I could learn so that is why I am here. I am hoping others might see improvements (without modifying the version text strings themselves).

 

LaRetta

Shaving a (squirming) yak  :-)

Versioning.fp7.zip

Link to comment
Share on other sites

Because in tier2, (the second set of 000), 10 is greater than 1.  Each section is treated as whole number (not decimal).  This is the only way I could work it out. 

 

Thank you for the link - I shall study it!  No, this is OUR files which are held to this standard so I was able to pin the pattern.

 

Max 3 numeric positions in each tier.  Not sure how else to clarify it.  Each group is a tier (there is probably a better name) and each tier consists of up to 3 numbers or zeros.  


And when you look at the corresponding calculation for it, you will see the extra zero, making the checkVersion calculation larger.

Link to comment
Share on other sites

Close but the first one is wrong.  cCurrentVersion shows 2.  But the currentVersion is 2.  And it is always in the leftmost tier (so tier 1).  So it needs to be multiplied by 1000000000 so it bumps into position.  So let's take the example (record 8) with:  2.3.0 build 11

 

This would be

 

tier1 = 2 * 1000000000 +

tier2 = 3 * 1000000 +

tier3 = 0 * 1000 +

tier4 = 11

 

Now you stack them and add.  Your other calculations look right.  :-)


I know my CF was ugly.  


BTW, I had note on record 6 wrong - it should say "Needs updating but LESS than minimum version"

 

Also I just correct this cDoUpdate calc and reverse it into a true span:

cNumCheckVer  ≥ cNumMinVer
and
cNumCheckVer   <   cNumCurrVer

I just couldn't let it go.


Oh wait!!  You have decimals!  My darned eyes!!  I'll get back to you!!


That's it!!  Oh what fun!  Now please show me how you did it!!  Where is Daniele?  I love this stuff!

 

Might it be because I multiplied but you divided?  Tickles me!


I knew mine felt kludgy!

Link to comment
Share on other sites

This is the calculation I used (result is Number) =

Let ( [
words = Substitute ( Filter ( version ; ". 0123456789" ) ;  "." ; " " ) ;
word1 = LeftWords ( words ; 1 ) ;
word2 = MiddleWords ( words ; 2 ; 1 ) ;
word3 = MiddleWords ( words ; 3 ; 1 ) ;
word4 = MiddleWords ( words ; 4 ; 1 )
] ;
word1 + word2 * 10^-3 + word3 * 10^-6 + word4 * 10^-9
)

Or, if you prefer:

...
] ;
word1 + word2 / 10^3 + word3 / 10^6 + word4 / 10^9
)

It could be turned into a recursive function, but I wanted to make sure I got the rules right.

 

---

 

If you prefer to work with integers, like in your version, you could also do:

...
] ;
word1 * 10^9 + word2 * 10^6 + word3 * 10^3 + word4
)
Edited by comment
  • Like 1
Link to comment
Share on other sites

I first thought of using xWords but I remembered that decimal between numbers are not word separators so would group as a single word.  I switched tactics to xValues without thinking it further but instead, you solved it in your Substitute() quite nicely! 

 

You didn't bother wrapping with GetAsNumber() since you remembered (unlike me) that you can add 010 just fine without removing leading zeros.  I knew it felt convoluted when I wrote it.

 

I certainly like 10^9 better than all my zeros and you came at it from the opposite end!  Throwing in the alternate just for fun adds another layer to the understanding.  ROCKIN' ... This is exactly why I love being in this forum.  

 

Hey, if you would write a CF, that would be great.  Regardless, you nailed the issue in your usual style.  Thank you.  :-)


LOL, I see your third alternate.  This is cool because it really provides several approaches which helps the principles sink in!  Truly!   :yep:

Link to comment
Share on other sites

To do this recursively, you could try something like:

 

 

VersionAsNum ( version ) =

Let ( [
words = Substitute ( Filter ( version ; ". 0123456789" ) ;  "." ; " " ) ;
countWords = WordCount ( words ) ;
word = RightWords ( words ; 1 ) ;
exp = -3 * ( countWords - 1 )
] ;
word * 10^exp
+
Case ( countWords > 1 ; VersionAsNum ( LeftWords ( words ; countWords  - 1 ) ) )
)

The flaw here is repeating the "clean-up" part (Substitute and Filter) needlessly on each iteration; it would be better to break this part out to the calling function.

  • Like 1
Link to comment
Share on other sites

This is, of course, perfect! So for anyone else reading, this is how the custom function was modified:

VersionAsNum ( version ) =

/* 
by Comment (Michael Horak)
version field should be pre-processed with: Substitute ( Filter ( Version ; ". 0123456789" ) ;  "." ; " " ) 
*/
Let ( [
countWords = WordCount ( version ) ;
word = RightWords ( version ; 1 ) ;
exp = -3 * ( countWords - 1 )
] ;
word * 10^exp
+
Case ( countWords > 1 ; versionAsNum ( LeftWords ( version ; countWords - 1 ) ) )
)

And the cVersion calculation was modified to:

Let ( words = Substitute ( Filter ( Version ; ". 0123456789" ) ;  "." ; " " ) ; versionAsNum ( words ) )

If anything should be adjusted, please let me know.

Would you explain your logic on this for us? exp = -3 * ( countWords - 1 )

Exp is exponent so you are determining the power number. You are figuring the number of groups of 3 ( less 1) then multiplying times -3 to get the number of decimal places to slide ... is that right?  I viewed the result of it individually and I see what it does - but it remains a bit fuzzy.  Can you explain its logic in YOUR words instead ? That will nail it for me.

 

EDITED: Sorry your CF looks odd.  I felt it important to include the notation about the author and how the field should be pre-processed, right within the custom function itself, but it seems the code didn't like having the comment inside it and it bombed on me.  

Link to comment
Share on other sites

Would you explain your logic on this for us? exp = -3 * ( countWords - 1 )

 

It's just an extension of the decimal system - or any other-base system where the position of the symbol determines its value.

 

In the decimal system, the arrangement "1,234.5" is read as "one thousand, two hundred, thirty-four and five tenths", which arithmetically comes down to =

1 * 10^3  +  2 * 10^2  +  3 * 10^1  +  4 * 10^0  +  5 * 10^-1

IOW, each symbol (digit) represents a value of 0..9, times 10 to the power of the position of the symbol (relative to the decimal point).

 

Here we have the same thing, except that we look at each word as a symbol that represent a value of 0..999, so each position's value is a power of 1000. For example, the number 1,234.567 could be written as =

1 * 1000^1  +  234 * 1000^0  +  567 * 1000^-1

which could be shortened to:

1 * 10^3  +  234 * 10^0  +  567 * 10^-3

Now, since we don't know in advance the number of words we'll have, and since we want to bring each version number to a common scale in order to allow comparisons, we'll  start with the rightmost word and assign it a position in such way that the leftmost word will always end up in the first position left of the decimal point - i.e. the position of units.

 

For example, given these three words "1 234 567" we want to end up with 1.234567 which is comparable with 1.234 obtained from "1 234" and with 1.23456789 obtained from "1 234 567 890". So eventually the rightmost word's position (and therefore its value) is given by the count of words to the left of it. This is a convenient way to put it, because then we can recurse without keeping track of where we are in the process. This would not be possible if the words were processed from left to right.

  • Like 1
Link to comment
Share on other sites

This is why I study here.  

 

For me, I first need a problem and then try to intently solve it on my own which sets the framework for the issues involved.  When then shown an excellent solution backed with logical explanation (as only you can provide), the principles plant deeply AND I can truly appreciate the beauty of the solution even more.  That explained You explained it perfectly!!

 

Thank you so much for all you give us on this forum.

Edited by LaRetta
Link to comment
Share on other sites

  • 1 month later...

The flaw here is repeating the "clean-up" part (Substitute and Filter) needlessly on each iteration; it would be better to break this part out to the calling function.

 

Would you consider this post again for me?  We are entering the file's version in a one-record developer table in each file.  I had originally planned for script to run the comparisons (see prior file for example script) so calculations would be unnecessary but the script leaves me chilled ... why repeatedly calculate these values in each file when it would open up possibility of omission, calculation error, or script break?  

 

I am not crazy about any portion of a CF (to properly function) be outside of the CF nor do I like CFs that require other CFs but if I AM going to hold a required piece of a CF outside of the CF, it makes more sense (I believe) to use a single indexed, self-sustaining calculation in each file for this piece.  And if the CF notes the requirement about the pre-processing, it feels safe enough but ...

 

Taking this further, couldn't we include the Substitute(Filter()) inside the CF but only have it process that portion once if we handle it this way?

[ custom function removed because it was not worthwhile ]

 

It seems a small thing but even small things count and both changing the CF or using calculation instead feels safer in this instance than my original idea of handling it all via script.

I tried to bold the areas of change from the prior CF but they would not take.  It comes in as textVersion, is pre-processed into 'version' and then 'version' is what is iterated in the following calls. 

Edited by LaRetta
Link to comment
Share on other sites

Taking this further, couldn't we include the Substitute(Filter()) inside the CF but only have it process that portion once if we handle it this way?

 

Oh.  No of course it wouldn't work that way.  In fact, there are other times I want to use CF to iterate and this same issue comes up - where I don't want it to process the original input.  Maybe I could use a counter ... yes, I know, YUK.  Well, I wanted to check anyway; just because I can't see the way around something doesn't mean it isn't possible at all.  :-)

BTW, a bit of background:  There are served files and iPad files and they both are separated which is why coordinating the versions is critical.

Link to comment
Share on other sites

A few thoughts, in random order:

 

1. If you control the version number, why don't you simplify the numbering system?

 

2. There's nothing inherently wrong in using a counter (or a flag to signify that pre-processing has been done); however, the processing required to consult the counter (or the flag), compared to the processing required to clean up the input (at least in this case) is practically the same.

 

3. There's nothing wrong in using a calling CF. In fact, if you ever looked at some of Shawn's files, he had custom functions calling each other in chains. That said, I too am in favor of concentrating the logic required for a task in one place, as much as possible. And would I be wrong to assume that this is going to be used in a script?

Link to comment
Share on other sites

1. If you control the version number, why don't you simplify the numbering system?

 

I considered making that suggestion.  However, to provide the maximum flexibility in a solution which is merging more than a single client where existing versioning already exists in their files, I didn't see a reason since it would potentially mean:

  1. Prior versioning compared to a new nomenclature would be off in viewing backups or older files.
  2. During the transition from old versions to new, this type of translation would be required anyway.
  3. It would change what is displayed to the clients which would be confusing.

I considered the reverse as well ... using number but providing a calculation whereby versions could be formatted as a client might expect.

 

Yes, this would be handled via script at each file's startup.  The layout name and table occurrence name will both be the same and the script could be copy/pasted into each file.  If this script failed upon file open it would be trapped anyway.  So you think this is an instance where best to leave it alone and in script?  I want light footprint but at same time, I want safe, consistent and flexible process throughout.

 

edited: portions in blue

Link to comment
Share on other sites

BTW, I knew you were going to ask why not change the process instead ... and at the start of this discussion, we had not decided upon a complete rewrite so my approach has changed slightly since then.  :-)

Link to comment
Share on other sites

This topic is 3547 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 account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...

Important Information

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