Filemaker calculation to display commas properly in a list - filemaker

I have a list of items that I need to appear in a report if they are tagged. As you can see by the code below it's nothing more than a list of numbers separated by commas. The problem is if #1 isn't in the list, then the list is always preceded by a comma. I can move the commas to the end but then I'll have the same problem after the last number if it's not the last number in my calculation. Does anyone have any idea how to adjust this so the commas only appear between numbers? It would be great if I could get a period after the last number as well.
//Spares calc
If ( ${1} = "Spare" and panelSize ≥ 1 ; "1" ; "" ) &
If ( ${2} = "Spare" and panelSize ≥ 2 ; ", 2" ; "" ) &
If ( ${3} = "Spare" and panelSize ≥ 3 ; ", 3" ; "" ) &
If ( ${4} = "Spare" and panelSize ≥ 4 ; ", 4" ; "" ) &

You can use the List function for this, since it excludes empty items. Just pass your numbers and empty strings as parameters to List, and then replace the newlines with commas, and tack a period on the nend.
Substitute ( List("1" ; "" ; "3" ; "4") ; "¶" ; ", ") & "."
The above results in:
1, 3, 4.
In your example, you can use Case statements as arguments to List:
Substitute ( List(
Case ( ${1} = "Spare" and panelSize ≥ 1 ; "1" ) ;
Case ( ${2} = "Spare" and panelSize ≥ 2 ; "2" ) ;
Case ( ${3} = "Spare" and panelSize ≥ 3 ; "3" ) ;
Case ( ${4} = "Spare" and panelSize ≥ 4 ; "4" ) ;
) ; "¶" ; ", ") & "."

You're probably going about what you're doing the hard way, but without seeing more of the detail, just change your calc to :
If ( ${1} = "Spare" and panelSize ≥ 1 ; "1, " ; "" ) &
If ( ${2} = "Spare" and panelSize ≥ 2 ; "2, " ; "" ) &
If ( ${3} = "Spare" and panelSize ≥ 3 ; "3, " ; "" ) &
If ( ${4} = "Spare" and panelSize ≥ 4 ; "4, " ; "" )
Then after that you can can change the last ", " to a "." but you may need to check how many ( if any ) appear in the final value first. Use the Let function to put the result into a variable that you can manipulate later.

Related

Declaring sets of anonymous arrays in perl

I'm trying to generate arrays for each of the agregations of cells in suduko. I seem to have fixed the problem, but don't understand what my earlier alternatives are doing.
I get the answer I expected if I write for instance:
#row = ( [], [], [], [], [], [], [], [], [] ) ;
I had expected
#row = ( [] ) x 9 ;
to behave the same. I also tried, which did better
#row = ( [] x 9 ) ;
Only the first element comes out strange with this, in two of the arrays. With the first rejected form I get all 81 elements in each array
I did wonder if the last form was actually legal?
# prob.pl - Show problem with repeat anonymous arrays
#
# ###################################################
#row = ( [] x 9 ) ;
#col = ( [] x 9 ) ;
#box = ( [] x 9 ) ;
for ( $i = 0 ; $i < 81 ; $i ++ ) {
push( #{$row[ row($i) ]}, $i ) ;
push( #{$col[ col($i) ]}, $i ) ;
push( #{$box[ box($i) ]}, $i ) ;
}
for ( $i = 0 ; $i < 9 ; $i ++ ) {
print STDERR "\#{\$row[$i]} = #{$row[$i]}\n" ;
print STDERR "\#{\$col[$i]} = #{$col[$i]}\n" ;
print STDERR "\#{\$box[$i]} = #{$box[$i]}\n" ;
}
sub row {
my( $i ) = #_ ;
int( $i / 9 ) ;
}
sub col {
my( $i ) = #_ ;
$i % 9 ;
}
sub box {
my( $i ) = #_ ;
int( col( $i ) / 3 ) + 3 * int( row( $i ) / 3 ) ;
}
Answer in two parts: first part is a simple view of what is happening and what you should do to fix it. Second part tries to explain this weird behavior you're getting.
Part 1 - simple explanations
All forms are legal; they are just not equivalent and probably don't do what you expect. In such case, Data::Dumper or Data::Printer are your friends:
use Data::Printer;
my #a1 = ( [] x 9 );
p #1;
Prints something like
[
[0] "ARRAY(0x1151f30)ARRAY(0x1151f30)ARRAY(0x1151f30)ARRAY(0x1151f30)ARRAY(0x1151f30)ARRAY(0x1151f30)ARRAY(0x1151f30)ARRAY(0x1151f30)ARRAY(0x1151f30)"
]
Quoting the doc of x (the "repetition operator", if you need to search for it):
In scalar context, or if the left operand is neither enclosed in parentheses nor a qw// list, it performs a string repetition.
([] x 9 calls x in scalar context)
On the other hand, when you do ([]) x 9, you get something like:
[
[0] [],
[1] var[0],
[2] var[0],
[3] var[0],
[4] var[0],
[5] var[0],
[6] var[0],
[7] var[0],
[8] var[0]
]
Quoting the doc again:
If the x is in list context, and the left operand is either enclosed in parentheses or a qw// list, it performs a list repetition. In that case it supplies list context to the left operand, and returns a list consisting of the left operand list repeated the number of times specified by the right operand.
What happens here, is that [] is evaluated before x is applied. It creates an arrayref, and x 9 then duplicates it 9 times.
Correct ways to achieve what you want would be either your first solution, or maybe, if you prefer something more concise (but still readable):
my #row = map { [] } 1 .. 9;
(since the body of the map is evaluated at each iteration, it creates indeed 9 distinct references)
Or, you could just not initialize #row, #col and #box and let autovivification creates the arrays when needed.
Part 2 - advanced explanation
You have some weird behavior with your program when you use ([] x 9). For simplicity, let me reproduce it with a simpler example:
use feature 'say';
#x = ([] x 5);
#y = ([] x 5);
#z = ([] x 5);
push #{$x[0]}, 1;
push #{$y[0]}, 1;
push #{$z[0]}, 1;
say "#{$x[0]}";
say "#{$y[0]}";
say "#{$z[0]}";
This program outputs:
1 1
1
1 1
Interestingly, removing the definition of #y (#y = ([] x 5)) from this programs produces the output:
1
1
1
Something fishy is going on. I'll explain it with two points.
First, let's consider the following example:
use Data::Printer;
use feature 'say';
say "Before:";
#x = "abcd";
p #x;
say "#{$x[0]}";
say "After:";
push #{$x[0]}, 5;
p #x;
say "#{$x[0]}";
say $abcd[0];
Which outputs
Before:
[
[0] "abcd"
]
After:
[
[0] "abcd"
]
5
5
When we do push #{$x[0]}, 5, #{$x[0]} becomes #{"abcd"}, which creates the arrays #abcd, and pushes 5 into it. $x[0] is still a string (abcd), but this string is also the name of an array.
Second*, let's look at the following program:
use Data::Printer;
#x = ([] x 5);
#y = ([] x 5);
#z = ([] x 5);
p #x;
p #y;
p #z;
We get the output:
[
[0] "ARRAY(0x19aff30)ARRAY(0x19aff30)ARRAY(0x19aff30)ARRAY(0x19aff30)ARRAY(0x19aff30)"
]
[
[0] "ARRAY(0x19b0188)ARRAY(0x19b0188)ARRAY(0x19b0188)ARRAY(0x19b0188)ARRAY(0x19b0188)"
]
[
[0] "ARRAY(0x19aff30)ARRAY(0x19aff30)ARRAY(0x19aff30)ARRAY(0x19aff30)ARRAY(0x19aff30)"
]
#x and #z contain the same reference. While this is surprising, I think that this is explainable: line 1, [] x 5 creates an arrayref, then converts it into a string to do x 5, and then it doesn't use the arrayref anymore. This means that the garbage collector is free to reclaim its memory, and Perl is free to reallocate something else at this address. For some reason, this doesn't happen right away (#y doesn't contain the same thing as #x), but only when allocating #z. This is probably just the result of the implementation of the garbage collector / optimizer, and I suspect it might change from a version to another.
In the end, what happens is this: #x and #z contains a single element, a string, which is identical. When you dereference $x[0] and $z[0], you therefore get the same array. Therefore, pushing into either $x[0] or $z[0] pushes into the same array.
This would have been caught with use strict, which would have said something like:
Can't use string ("ARRAY(0x2339f30)ARRAY(0x2339f30)"...) as an ARRAY ref while "strict refs" in use at repl1.pl line 11.
* note that for this second part, I am not sure that this is what happens, and this is only my (somewhat educated) guess. Please, don't take my word for it, and feel free to correct me if you know better.
The second form creates 9 copies of the same array reference. The third form creates a single array element consisting of a string like "ARRAY(0x221f488)" concatenated together 9 times. Either create 9 individual arrays with e.g. push #row, [] for 1..9; or rely on autovivification.

Why does this perl DateTime math produce unexpected results?

#!/usr/bin/perl
use DateTime;
$a = DateTime->new(year=>1952,month=>10,day=>21);
$b = DateTime->new(year=>2015,month=>10,day=>31);
$dif = $b-$a;
print $dif->years() ." ". $dif->months() ." ". $dif->days();
# result: 63 0 3
Where does it get the 3 days from? My expectation is 63 0 10.
#!/usr/bin/perl
use DateTime;
$a = DateTime->new(year=>1952,month=>11,day=>1);
$b = DateTime->new(year=>2015,month=>10,day=>31);
$dif = $b-$a;
print $dif->years() ." ". $dif->months() ." ". $dif->days();
# result 62 11 2
My expectation for this one is 62 11 31 or so.
I am trying to do some basic date of birth to age math. The month and year seem to work as I expect but the day seems unpredictable. I have read the CPAN documentation but I still do not understand.
$dif->years, $diff->months and in particular $diff->days do not do what you expect. From the DateTime::Duration documentation...
These methods return numbers indicating how many of the given unit the
object represents, after having done a conversion to any larger units. For
example, days are first converted to weeks, and then the remainder is
returned. These numbers are always positive.
Here's what each method returns:
$dur->years() == abs( $dur->in_units('years') )
$dur->months() == abs( ( $dur->in_units( 'months', 'years' ) )[0] )
$dur->weeks() == abs( $dur->in_units( 'weeks' ) )
$dur->days() == abs( ( $dur->in_units( 'days', 'weeks' ) )[0] )
If you find this confusing, so do I.
What you want is in_units.
# 63 0 10
say join " ", $dif->in_units('years', 'months', 'days');

how to remove special character "," from a string using perl

hi i have some data like below
S_ METHOD m0 : 47|8#0- (1,0) [0|0] ""
S_ CTRL m1 : 15|8#0- (0.01,-200) [0|0] ""
from above 2 lines i am trying to extract that are in curve brackets () i have written a perl script
my #temp_signal = split(":",$line);
my #signal= split(" ",#temp_signal[0]);
my #Factor_temp1 = split (" ",#temp_signal[1]);
my #factor_temp = split ('\(',#Factor_temp1[1]);
my #factor = chop(#factor_temp);
my #offset = split (",",#factor_temp);
print OUTFILE1 "#offset[0]\n";
print OUTFILE1 "$signal[1]\n";
but when am trying to print #offset[1] & #offset[0] its printing some other value which is not even exist in the line how can i get the values as
1 0
0.01 -200
You can use a regular expression match to extract what's inside parentheses separated by a comma:
if ( my #numbers = $line =~ /\((.*),(.*)\)/) {
print "$numbers[0] $numbers[1]\n";
}

Calculate number of days between two dates (without using modules)

I searched around for this a lot, and it appears all solutions are making use of some module or the other.
I have 2 dates in yyyymmdd format. I'd like to know of a simple way to calculate the number of days between these dates, without using any modules.
Example:
Date 1: 20130625
Date 2: 20130705
Number of days = 10
PS: I cannot use any modules due to restrictions on the server I will be running the script on. Perl version - 5.8.4
Expression returns 10.
(
Time::Piece->strptime('20130705', '%Y%m%d')
- Time::Piece->strptime('20130625', '%Y%m%d')
)->days
Time::Piece is part of the core Perl distribution since v5.9.5.
sub yyyymmdd_to_rata_die {
use integer;
my ( $y, $m, $d ) = $_[0] =~ /\A([0-9]{4})([0-9]{2})([0-9]{2})\z/
or return;
my $adj;
# make month in range 3..14 (treat Jan & Feb as months 13..14 of prev year)
if ( $m <= 2 ) {
$y -= ( $adj = ( 14 - $m ) / 12 );
$m += 12 * $adj;
}
elsif ( $m > 14 ) {
$y += ( $adj = ( $m - 3 ) / 12 );
$m -= 12 * $adj;
}
# add: day of month, days of previous 0-11 month period that began w/March,
# days of previous 0-399 year period that began w/March of a 400-multiple
# year), days of any 400-year periods before that, and 306 days to adjust
# from Mar 1, year 0-relative to Jan 1, year 1-relative (whew)
$d += ( $m * 367 - 1094 ) / 12 + $y % 100 * 1461 / 4 + ( $y / 100 * 36524 + $y / 400 ) - 306;
}
print yyyymmdd_to_rata_die(20130705) - yyyymmdd_to_rata_die(20130625);
Its simple:
1) for each date:
1.1) turn years into ydays
1.2) turn months into mdays
1.3) make sum days + mdays + ydays
2) substract that two values

Manipulating digits

This is a program which grabs lines which contains the $position AND $amino value in the first two columns.
Code:
#!/usr/bin/perl
my $id = $ARGV[0];
my $position = $ARGV[1]; # POSITION OF THE RESIDUE
my $amino= $ARGV[2]; #THREE LETTER AMINO ACID CODE IN CAPITALS
my #grabbed;
open (FILE, $id.$amino.$position.".hb2");
#CREATES AN ARRAY WITH ONLY THE VALUES FROM THE HB2 FILE. REMOVES THE HEADER OF THE FILE.
while (<FILE>) {
if (/^-/) {
push #grabbed, $_;
while (<FILE>) {
last if /^$/;
push #grabbed, $_;
}
}
}
close (FILE);
for ( #grabbed ) {
my #f = split;
if (( $f[2] == "-"."00".$position."-".$amino ) or ($f[0] == "-"."00".$position."-".$amino)) {
push #line, $id.$amino.$position, " ",$_;
}
}
print #line;
Partial input data :
-0007-ARG NH2 -0009-GLN OE1 3.24 SS 2 6.00 143.3 2.38 105.9 95.8 1 #CASE 1
-0008-GLU N -0008-GLU OE1 2.62 MS 0 -1.00 120.8 1.96 102.3 103.4 2
-0011-ILE N -0117-ARG O 2.87 MM 106 4.90 144.0 2.00 127.5 139.0 3
-0117-ARG N -0011-ILE O 2.75 MM 106 4.90 160.4 1.79 153.2 148.6 4 #CASE 2
-0016-SER N -0012-THR O 2.89 MM 4 6.00 156.2 1.95 149.8 154.8 5 #CASE 3
-0017-ALA N -0013-LEU O 3.10 MM 4 6.24 152.8 2.17 143.4 149.7 6
-0018-GLU N -0014-ARG O 3.04 MM 4 6.24 154.1 2.11 147.2 154.2 7
-0019-ILE N -0015-GLY O 2.90 MM 4 6.16 155.8 1.96 150.7 156.2 8
-0016-SER OG -0188-THR OG1 2.72 SS 172 5.92 172.0 1.73 98.9 99.6 9
-0188-THR OG1 -0016-SER OG 2.72 SS 172 5.92 163.7 1.75 116.4 115.1 10
Question :
In order to generalize the program I made the match as :
( $f[2] == "-"."00".$position."-".$amino ) or ($f[0] == "-"."00".$position."-".$amino)
The format is always four digits after "-" before $amino (-0188-THR). I suddenly realized that my code wouldnt work if the $position input is "one digit(like CASE 1)" or "three digit (like CASE 2, column 1)". Since I hard coded it as format as "-" followed by two zeros and THEN position, it has to always be two digit input to work.
I am stumped to generalize this code so that I could put in 1/2/3 digits. The remaining digits would always be replaced by zeros.
You can format the string using sprintf:
my $mstring = sprintf("-%04d-%s", $position, $amino);
if ( ($f[2] eq $mstring) or ($f[0] eq $mstring) ) {
# ...
}
Here, %04d adds 0's to the left of position to make it 4 digits long.
First, == operator in perl used only for comparing arithmetic expressions
To compare strings you should use eq operator
Second, to format strings from digits you can use sprintf function.
if ($f[2] eq "-".sprintf("%04d", $position)."-".$amino ...