I am to display New Zealand and Australian bank accounts in reports, formatted according to a custom format supplied by the user. For example, NZ bank accounts can be 00-0000-0000000-000 with the last digit (bank account suffix) being optional. There are two parts of the format:
Placement of dashes
2 digit prefix
Sample formatted bank accounts can be 01-1234-1234567-55 and 01-1234-1234567-002. The bank accounts are stored in the database without any formatting. When I tried String.Format("{0:00-0000-0000000-00#}",121234123456712) it does not return the expected 12-1234-1234567-12 but 01-2123-4123456-712.
Understandably I could always test the length of the bank account and do a switch statement, but the format is user defined.
The following ensures the dashes are placed correctly and the suffix is correct:
// ensure there is a format to use and a bank account is present
if (bankaccountformat != "" && bankaccountformat.Contains('-') && bankaccount != "")
{
int i = 0;
foreach (char dash in bankaccountformat)
{
// add dash in bank account, if bank account is long enough
if (dash == '-' && bankaccount.Length > i)
{
bankaccount = bankaccount.Insert(i, "-");
}
i++;
}
}
The issue is not if the account is valid or not, it is the formatting. Please let me know of a better way to format the account.
You do not mention which programming language you use. Anyhow, it seems you use an array. That can be tricky.
In BASH I would use string manipulation, like in this script (fmat):
#!/bin/bash
#only numbers allowed
[[ ! "$1" =~ ^[0-9]+$ ]] && echo "Error: enter numbers only" && exit
s=$1
l=${#s}
r=l-13
#minimal number of digits is 13, maximum 16
[[ r -lt 0 || r -gt 3 ]] && echo "Error: enter 13-16 digits" && exit
#first 13 digits
a=${s:0:2}"-"${s:2:4}"-"${s:6:7}
#when 14-16 digits
a=$a"-"${s:13:r}
echo $a
fmat 011234123456755 gives 01-1234-1234567-55
fmat 0112341234567002 gives 01-1234-1234567-002
fmat 0112341234567 gives 01-1234-1234567
If you use something else than BASH, then at least this gives some hints.
Related
I am confused with a tcsh shell script issue. (for work, no choice in shell, I'm stuck with it)
The enableThingN items below are shell enviroment variables set by other things before running this csh script, using tcsh shell. These are not set within the same script here at all, only evaluated here.
Error message is:
enableThing1: Undefined variable.
Code is:
if ( ( $?enableThing1 && ($enableThing1 == 1) ) || \
( $?enableThing2 && ($enableThing2 == 1) ) || \
( $?enableThing3 && ($enableThing3 == 1) ) || \
( $?enableThing4 && ($enableThing4 == 1) ) ) then
set someScriptVar = FALSE
else
set someScriptVar = TRUE
endif
So, as I understand things, the first part of the big if condition is to check if enableThing1 is defined at all or not, using the $?enableThing1 magic. If it is defined, then move on and check the value is 1 or something else. If not defined, then skip the ==1 part of the check for the same shell variable, and move on to see if enableThing2 is defined at all or not, and so on.
As it seems like I am checking for existence, and intend to avoid checking value if it is not defined at all, where have I gone wrong?
I have searched here on stackoverflow and on Google at large, but there are few results and don't get me to an answer, such as:
https://stackoverflow.com/questions/16975968/what-does-var-mean-in-csh
An if statement to check the value of the variable requires that the variable exists.
if ( ( $?enableThing1 && ($enableThing1 == 1) ) || \
# ^ this will fail if the variable is not defined.
So the if condition turns into
if ( ( 0 && don'tknowaboutthis ) || \
and it falls flat.
Assuming you don't want an if ladder, and functionality to add to this list of variables to check for, you can try the following solution:
#!/bin/csh -f
set enableThings = ( enableThing1 enableThing2 enableThing3 enableThing4 ... )
# setting to false initially
set someScriptVar = FALSE
foreach enableThing ($enableThings)
# since we can't use $'s in $? we'll have to do something like this.
set testEnableThing = `env | grep $enableThing`
# this part is for checking if it exists or not, and if it's enabled or not
if (($testEnableThing != "") && (`echo $testEnableThing | cut -d= -f2` == 1 )) then
# ^ this is to check if the variable is defined ^ this is to take the part after the =
# d stands for delimiter
# for example, the output of testEnableThing, if it exists, would be enableThing1=1
# then we take that and cut it to get the value of the variable, in our example it's 1
# if it exists and is enabled, set your someScriptVar
set someScriptVar = TRUE
# you can put a break here since it's irrelevant to check
# for other variables after this becomes true
break
endif
end
This works because we are only working with one variable, "testEnableThing", which is always defined due to the way this works. It can be a blank string, but it will be defined so our if statement won't fall flat.
Hope this solves it for you.
Creating a schedule where I introduce a word and the program returns the info I need is what I want. I mean, if I write the word "monday" I would like a response with the subjects I have that day. I did this (very brief example, I have more subjects):
x = int(input("Day of the week: "))
if x == 2:
x = 0
print('9:00-11:00 Biology - Classroom C4B \n11:00-13:00 Maths- Classroom C5')
elif x == 3:
print('11:00-13:00 Physics - Classroom C4B')
This works, but the problem is that I do not want to enter numbers but words. I also tried with eval and works too. However, in that case, I must enter a word between '' because eval reads strings and that is not what I want. How can I improve my program?
Thanks in advance (Python 3)
And because you do not use raw_input( )?
input() actually evaluates the input as Python code.
And in your code x = int(input("Day of the week: ")) only accepts numerics inputs. raw_input() returns the verbatim string entered by the user.
day = raw_input("Day of the week: ")
if day == "monday":
print('9:00-11:00 Biology - Classroom C4B \n11:00-13:00 Maths- Classroom C5')
elif day == "tuesday":
print('11:00-13:00 Physics - Classroom C4B')
mathematical range,for example:
greater or equal to 50 and smaller than 100 (>=50 && < 100)
smaller than 10 or greater than 40 (<10 || >40)
I have been thinking about how to represent mathematical range in a file and database, the range may be input by non programmer and I need to keep the input simple,but at another side, it also need to keep the input easy to convert to data and easy to check error input e.g.:"<10 || >100" seems the most simple but it is harder for me to parse the string to get the data,also need to consider input format error
I have been considering some input methods,using >=50 && < 100 as example,which is in key value form:
1.using 1 string to represent whole range:
<rangeInString>=50 && < 100</rangeInString>
2.separate 2 strings,one represent lower bound and another one represent upper bound,then parse each string in program:
<lowerBound> >=50 </lowerBound>
<upperBound> <100 </upperBound>
3.separate lower and upper bound,also separate the sign from number:
<lowerBound>
<sign> >= </sign>
<data>50</data>
</lowerBound>
<upperBound>
<sign> < </sign>
<data>100</data>
</upperBound>
4.separate lower bound and upper bound,also separate sign, and also separate the case that if includes the equal condition:
<lowerBound>
<sign> > </sign>
<isIncludeEqual>true</isIncludeEqual>
<data>50</data>
</lowerBound>
<upperBound>
<sign> < </sign>
<isIncludeEqual>false</isIncludeEqual>
<data>100</data>
</upperBound>
5.auto detect using "&&" or "||",e.g.:>= A with < B,if A < B,must be "&&" e.g.(>= 50 && <100),otherwise it is "||" e.g.(>= 100 || <50):
<A>
<sign> > </sign>
<isIncludeEqual>true</isIncludeEqual>
<data>50</data>
</A>
<B>
<sign> < </sign>
<isIncludeEqual>false</isIncludeEqual>
<data>100</data>
</B>
6.use a field "isAnd" to separate >=50 && < 100 (true) and <=50 || > 100 (false)instead of using field sign "<" and ">" :
<lowerBound>
<isIncludeEqual>true</isIncludeEqual>
<data>50</data>
</lowerBound>
<upperBound>
<isIncludeEqual>false</isIncludeEqual>
<data>100</data>
</upperBound>
<isAnd>true</isAnd>
7.other data model...
I need to consider somethings:
1.easy for non programmer to input
2.easy to convert or parse to data into program
3.easy to check error ,for example,parse string increase the complexity of converting data and checking incorrect format,also there may have other incorrect format,e.g.:<=50 && >100 should not be valid, I may allow auto detect using "&&" or "||" by the sign of input,but it may increase the complexity of the code
can anyone have idea?
Why "encode" it? There's no benefit or need and some hassle to use it.
Just store the exclusive range end values
low_end int,
high_end int,
You can then convert these raw values to useable expressions either in SQL or application code. You don't need to consider inclusive values because "n exclusive" === "n inclusive - 1" for low end and "n exclusive" === "n inclusive + 1" for high end.
Here's an SQL implementation:
where (low_end is null or col > low_end)
and (high_end is null or col < high_end)
If the range end values need to be floating point numbers, you'll need a little more:
low_end int,
low_inclusive boolean,
high_end int,
high_inclusive boolean,
And more code:
where (low_end is null or col > low_end + case when low_inclusive then 0 else 1 end)
and (high_end is null or col < high_end - case when high_inclusive then 0 else 1 end)
This is a good question, what about a combination of interval notation as suggested by Gordon and a given character for infinity. This combined with separate fields (or a parsing algorithm) could accomplish the task of defining any range.
For example, the range (3<x<20) could be written as (3,20). The range (x<=10 || x>30) could be written as the combination of
(-_,10],(30,_).
Where _ represents infinity. Or use the actual Infinity symbol character, ∞, Unicode U+221E.
This way would be pretty clear for those with a mathematics background, I believe, and would provide infinite flexibility.
I hope you find this helpful.
PostgreSQL does ranges natively.
The representation looks like this:
[low, high)
[ or ] = inclusive
( or ) = exclusive
Unbounded looks like this: [low-value, infinity]
http://www.postgresql.org/docs/9.4/static/rangetypes.html
Specifically addressing your options:
Why represent it in a format that you have to parse? A case could be made that you store it in a format that your code can parse, but what if you need to access it with a different programming language?
Same as 1.
Getting close, but you would need to subsume the bounds within a range object that includes && or ||. Also, no need for element, which is implied by "lower" and "upper" and could be replaced by an inclusive flag like you have in 4.
No need for
Unnecessary abstraction...it's just a range
That could work
Other data model:
The data is structured, so could work in json, xml, relational, or even as a set of semantic triples.
Short version:
Is there a way to tell awk to round to 2 decimal places during the consolidation, not during the printing?
Long version:
I have an incoming file in the format below. I should get the net balances per currency and if the net is NOT zero, print the result in two columns: net balances less than zero go to neg_bal column and positive balances go to pos_bal column. For some reason, the USD column is still being printed despite netting to zero
JPY||170
JPY||40
USD|-42.61|
USD|-166.27
USD||42.61|
GBP|-20|
EUR||18.7
USD||174.6|
USD|-8.33||
EUR|-30.6|
GBP||100
JPY|-210|
Here is the code am using:
#!/bin/awk -f
BEGIN {
FS="|";
}
{
bal[$1]+=$2+$3
ccy[$1]=$1
}
END {
for (i in ccy)
{
if (bal[i] >0 )
{
pos_bal = bal[i]
neg_bal = 0
}
else
{
neg_bal = bal[i]
pos_bal = 0
}
if (bal[i] != 0 )
{
printf "%s|%.2f|%.2f\n",ccy[i],neg_bal,pos_bal
}
}
}
Result (notice JPY is not displayed since it nets to zero):
awk]$ ./scr1 file1
EUR|-11.90|0.00
USD|0.00|0.00
GBP|0.00|80.00
If I increase the decimal places to say, 20, I see that the USD net amount is not really zero. (Why is this, btw? Even excel gives a net of -1.59872E-14)
awk]$ ./scr1 file1
EUR|-11.90000000000000213163|0.00000000000000000000
USD|0.00000000000000000000|0.00000000000001243450
GBP|0.00000000000000000000|80.00000000000000000000
Is there a way to tell awk to round to 2 decimal places during the
consolidation, not during the printing?
Yes: multiply by 100 and convert to int. Then divide by 100 when you're ready to print.
(In other words, count pennies instead of dollars.)
I have an SQL file which will give me an output like below:
10|1
10|2
10|3
11|2
11|4
.
.
.
I am using this in a Perl script like below:
my #tmp_cycledef = `sqlplus -s $connstr \#DLCycleState.sql`;
after this above statement, since #tmp_cycledef has all the output of the SQL query,
I want to show the output as:
10 1,2,3
11 2,4
How could I do this using Perl?
EDIT:
I am using the following code:
foreach my $row (#tmp_cycledef)
{
chomp $row;
my ($cycle_code,$cycle_month)= split /\s*\|\s*/, $row;
print "$cycle_code, $cycle_month\n";
$hash{$cycle_code}{$cycle_month}=1
}
foreach my $num ( sort keys %hash )
{
my $h = $hash{$num};
print join(',',sort keys %$h),"\n";
}
the fist print statement prints:
2, 1
2, 10
2, 11
2, 12
3, 1
3, 10
3, 11
but the out is always
1,10,11,12
1,10,11,12
1,10,11,12
1,10,11,12
1,10,11,12
1,10,11,12
1,10,11,12
Well, this one is actually how you might do it in perl:
# two must-have pragmas for perl development
use strict;
use warnings;
Perl allows for variables to be created as they are used, $feldman = some_function() means that you now have the variable $feldman in your local namespace. But the bad part about this is that you can type $fldman and take a long time finding out why what you thought was $feldman has no value. Turning on strictures means that your code fails to compile if it encounters an undeclared variable. You declare a variable with a my or our statement (or in older Perl code a use vars statement.
Turning on warnings just warns you when you're not getting values you expect. Often warnings tends to be too touchy, but they are generally a good thing to develop code with.
my %hash; # the base object for the data
Here, I've declared a hash variable that I creatively called %hash. The sigil (pronounced "sijil") "%" tells that it is a map of name-value pairs. This my statement declared the variable and makes it legal for the compiler. The compiler will warn me about any use of %hsh.
The next item is a foreach loop (which can be abbreviated "for"). The loop will process the list of lines in #tmp_cycledef assigning each one in turn to $row. ( my $row).
We chomp the line first, removing the end-of-line character for that platform.
We split the line on the '|' character, creating a list of strings that had been separated by a pipe.
And then we store it in a two-layered hash. Since we want to group them by at least the first number. We could do this by array, and create an array at the location in the hash like so: push #{$hash{$key}}, $val, but I typically want to collapse duplicates (not that there were any duplicates in your sample.)
Here:
foreach my $row ( #tmp_cycledef ) {
chomp $row; # removes the end-of-line character when present.
my ( $key, $val ) = split /\|/, $row;
# One of the best ways to merge lists is a presence-of idea
# with the hash holding whether the value is present
$hash{$key}{$val} = 1;
}
Once we have the data in the structure, we need to iterate both level of hash keys. You wanted to separate the "top level" numbers by lines, but you wanted the second numbers concatenated on the same line. So we print a line for each of the first numbers and join the list of strings stored for each number on the same line, delimited by commas. We also sort the list: { $a <=> $b } just takes to keys and numerically compares them. So you get a numeric order.
# If they were alpha keys our sort routine, we would just likely say sort keys %hash
foreach my $num ( sort { $a <=> $b } keys %hash ) {
my $h = $hash{$num};
print "$num ", join( ',', sort { $a <=> $b } keys %$h ), "\n";
}
As I said in the comments, sort, by default, sorts in character order so you can just say sort keys %hash.
To help you out, you really need to read some of these:
strictures
warnings
perldata
perlfunc -- especially my, foreach, chomp, split, keys, sort and join
And the data structure tutorial
Use a hash of arrays to collect all the values for a single key together, then print them out:
init hash
for each line:
parse into key|value
append value to hash[key]
for each key in hash: # you can sort it, if needed
print out key, list of values
If your input is sorted (as it is in the provided sample), you don't actually need to bother with the hash of arrays/hashes. The code is a bit longer, but doesn't require you to understand references and should run faster for large datasets:
#!/usr/bin/perl
use strict;
use warnings;
my #tmp_cycledef = <DATA>;
my $last_key;
my #values;
for (#tmp_cycledef) {
chomp;
my ($key, $val) = split '\|';
# Seed $last_key with the first key value on the first pass
$last_key = $key unless defined $last_key;
# The key has changed, so it's time to print out the values associated
# with the previous key, then reset everything for the new one
if ($key != $last_key) {
print "$last_key " . join(',', #values) . "\n";
$last_key = $key;
#values = ();
}
# Add the current value to the list of values for this key
push #values, $val;
}
# Don't forget to print out the final key when you're done!
print "$last_key " . join(',', #values) . "\n";
__DATA__
10|1
10|2
10|3
11|2
11|4