How to empty the $table every iteration drawn using Text::SimpleTable - perl

#!/tool/pandora64/bin/perl5.18.1
use strict;
use warnings;
use Text::SimpleTable; my $a=1;my $b=2; my $c=3; my $d=4; my #p_arr = (1,2);
my $table = Text::SimpleTable->new([1, "A"], [2, "B"], [3, "C"], [4, "D"]);
foreach my $p(#p_arr){
$table->row($a,$b,$c,$d);
$a=$a+1; $b=$b+1; $c=$c+1; $d=$d+1;
print "\nAfter $p iteration\n";
print $table->draw;
}
I need to print the table, but after the first iteration it is appending. How can I empty the table after every iteration?
Output I get:
After 1 iteration
.----+----+-----+------.
| A | B | C | D |
+----+----+-----+------+
| 1 | 2 | 3 | 4 |
'----+----+-----+------'
After 2 iteration
.----+----+-----+------.
| A | B | C | D |
+----+----+-----+------+
| 1 | 2 | 3 | 4 |
| 2 | 3 | 4 | 1 |
'----+----+-----+------'
Expected after 1st iteration:
.----------------
| A | B | C | D |
+----------------
| 1 | 2 | 3 | 4 |
+----------------
Expected 2nd iteration:
.----------------
| A | B | C | D |
+----------------
| 2 | 3 | 4 | 5 |

Simply move the call to new inside the foreach loop. This creates a new table each time through the loop:
use strict;
use warnings;
use Text::SimpleTable; my $a=1;my $b=2; my $c=3; my $d=4; my #p_arr = (1,2);
foreach my $p(#p_arr){
my $table = Text::SimpleTable->new([1, "A"], [2, "B"], [3, "C"], [4, "D"]);
$table->row($a,$b,$c,$d);
$a=$a+1; $b=$b+1; $c=$c+1; $d=$d+1;
print "\nAfter $p iteration\n";
print $table->draw;
}
Prints:
After 1 iteration
.----+----+-----+------.
| A | B | C | D |
+----+----+-----+------+
| 1 | 2 | 3 | 4 |
'----+----+-----+------'
After 2 iteration
.----+----+-----+------.
| A | B | C | D |
+----+----+-----+------+
| 2 | 3 | 4 | 5 |
'----+----+-----+------'
If you want the width of all the columns to be the same, you can use the same number in the call to new:
my $table = Text::SimpleTable->new([1, "A"], [1, "B"], [1, "C"], [1, "D"]);
The POD doesn't specify what the arguments to new are, so I had to look at the source code.

Related

Conditionally lag value over multiple rows

I am trying to find cases where one type of error causes multiple sequential instances of a second type of error on a vehicle. For example, if there are two vehicles, 'a' and 'b', and vehicle a has an error of type 1 ('error_1') on day 0, it can cause errors of type 2 ('error_2') on days 1, 2, 3, and 4. I want to create a variable named cascading_error that shows every consecutive error_2 following an error_1. Note that in the case of vehicle b, it is possible to have an error_2 without a preceding error_1, in which case the value for cascading_error should be 0.
Here's what I've tried:
vals = [('a',0,1,0),('a',1,0,1),('a',2,0,1),('a',3,0,1),('b',0,0,0),('b',1,0,0),('b',2,0,1), ('b',3,0,1)]
df = spark.createDataFrame(vals, ['vehicle','day','error_1','error_2'])
w = Window.partitionBy('vehicle').orderBy('day')
df = df.withColumn('cascading_error', F.lag(df.error_1).over(w) * df.error_2)
df = df.withColumn('cascading_error', F.when((F.lag(df.cascading_error).over(w)==1) & (df.error_2==1), F.lit(1)).otherwise(df.cascading_error))
df.show()
This is my result
| vehicle | day | error_1 | error_2 | cascading_error |
| ------- | --- | ------- | ------- | --------------- |
| a | 0 | 1 | 0 | null |
| a | 1 | 0 | 1 | 1 |
| a | 2 | 0 | 1 | 1 |
| a | 3 | 0 | 1 | 0 |
| a | 4 | 0 | 1 | 0 |
| b | 0 | 0 | 0 | null |
| b | 1 | 0 | 0 | 0 |
| b | 2 | 0 | 1 | 0 |
| b | 3 | 0 | 1 | 0 |
The code is generating the correct cascading_error value on days 1 and 2 for vehicle a, but not on days 3 and 4, which should also be 1. It seems that the logic of combining cascading_error with error_2 to update cascading_error only works for a single row, not sequential ones.

Why can memory leaking by cross-reference be solved by explicit reassignment in Perl?

Cross-reference causes memory leaking in Perl like this.
{
my #a = qw(a b c);
my #b = qw(a b c);
# both reference count are 1
push #a, \#b;
# #b reference count is 2(from #b and via #a)
push #b, \#a;
}
# #b reference count is 2(from via #a)
I understand memory leaking by cross-reference in this situation.
But the memory leaking can be resolve by explicit reassignment like this.
{
my #a = qw(a b c);
my #b = qw(a b c);
# both reference count are 1
push #a, \#b;
# #b reference count is 2(from #b and via #a)
push #b, \#a;
#a = ();
}
# why is #b reference count 0?
#a is lexical scope so I think even if there is no reassignment, #a's reference will be invalid but former cause memory leaking and later is not, why?
You start with
#a #b
| ARRAY | ARRAY
| REFCNT=2 | REFCNT=2
+-->+-----------+ +-->+-----------+
| | +-------+ | | | +-------+ |
| | | a | | | | | a | |
| | +-------+ | | | +-------+ |
| | | b | | | | | b | |
| | +-------+ | | | +-------+ |
| | | c | | | | | c | |
| | +-------+ | | | +-------+ |
| | | --------+ | | --------+
| | +-------+ | | +-------+ | |
| +-----------+ +-----------+ |
| |
+---------------------------------------+
If you were to exit the scope here, the reference counts would drop to one, and they would leak.
After #a = ();:
#a #b
| ARRAY | ARRAY
| REFCNT=2 | REFCNT=1
+-->+-----------+ +-->+-----------+
| | | | +-------+ |
| | | | | a | |
| | | | +-------+ |
| | | | | b | |
| | | | +-------+ |
| | | | | c | |
| | | | +-------+ |
| | | | | --------+
| | | | +-------+ | |
| +-----------+ +-----------+ |
| |
+---------------------------------------+
Note that #b's reference count went from two to one.
On scope exit, #a's reference count will drop to one, and #b's reference count will drop to zero.[1] This will free #b, which will cause #a's reference count to drop to zero. And that will free #a.
No cycle, so no memory leak.
At least in theory. In practice, what actually happens is a bit different as an optimization. But those are internal details that aren't relevant here.

How to adapt a query so that it can perform all possible combinations (cross join) between elements of the same table?

Considering the following table:
group | type | element
------+------+--------
1 | 1 | A
1 | 1 | B
1 | 2 | C
1 | 2 | D
1 | 3 | E
1 | 3 | F
2 | 4 | G
2 | 4 | H
2 | 5 | I
2 | 5 | J
2 | 5 | K
3 | 4 | L
3 | 4 | M
3 | 4 | N
3 | 5 | O
3 | 5 | P
3 | 6 | Q
3 | 7 | R
3 | 7 | S
3 | 7 | T
I need to select all possible combinations between elements of the element column, but filtering through the group column and grouping by column type.
Using the following query I can do this query by filtering through the elements of group 1 (this has types 1,2 and 3):
SELECT T1.*, T2.type, T2.element, T3.type, T3.element
FROM (SELECT * FROM test WHERE "group" = 1 AND type = 1) AS T1
CROSS JOIN (SELECT * FROM test WHERE "group" = 1 AND type = 2) AS T2
CROSS JOIN (SELECT * FROM test WHERE "group" = 1 AND type = 3) AS T3;
Obtaining the following result:
group | type | element | type | element | type | element
------+------+---------+------+---------+------+--------
1 | 1 | A | 2 | C | 3 | E
1 | 1 | A | 2 | C | 3 | F
1 | 1 | A | 2 | D | 3 | E
1 | 1 | A | 2 | D | 3 | F
1 | 1 | B | 2 | C | 3 | E
1 | 1 | B | 2 | C | 3 | F
1 | 1 | B | 2 | D | 3 | E
1 | 1 | B | 2 | D | 3 | F
Already to filter by the elements of group 2 I need to use another query reducing the amount of cross join because group 2 has fewer types (only types 4 and 5):
SELECT T1.*, T2.type, T2.element
FROM (SELECT * FROM test WHERE "group" = 2 AND type = 4) AS T1
CROSS JOIN (SELECT * FROM test WHERE "group" = 2 AND type = 5) AS T2;
Obtaining the following result:
group | type | element | type | element
------+------+---------+------+--------
2 | 4 | G | 5 | I
2 | 4 | G | 5 | J
2 | 4 | G | 5 | K
2 | 4 | H | 5 | I
2 | 4 | H | 5 | J
2 | 4 | H | 5 | K
And finally to select the elements filtering by the group 3, I need to increase the amount of cross join because this group has 4 types (4,5,6 and 7):
SELECT T1.*, T2.type, T2.element, T3.type, T3.element, T4.type, T4.element
FROM (SELECT * FROM test WHERE "group" = 3 AND type = 4) AS T1
CROSS JOIN (SELECT * FROM test WHERE "group" = 3 AND type = 5) AS T2
CROSS JOIN (SELECT * FROM test WHERE "group" = 3 AND type = 6) AS T3
CROSS JOIN (SELECT * FROM test WHERE "group" = 3 AND type = 7) AS T4;
Obtaining the following result:
group | type | element | type | element | type | element | type | element
------+------+---------+------+---------+------+---------+------+--------
3 | 4 | L | 5 | O | 6 | Q | 7 | R
3 | 4 | L | 5 | O | 6 | Q | 7 | S
3 | 4 | L | 5 | O | 6 | Q | 7 | T
3 | 4 | L | 5 | P | 6 | Q | 7 | R
3 | 4 | L | 5 | P | 6 | Q | 7 | S
3 | 4 | L | 5 | P | 6 | Q | 7 | T
3 | 4 | M | 5 | O | 6 | Q | 7 | R
3 | 4 | M | 5 | O | 6 | Q | 7 | S
3 | 4 | M | 5 | O | 6 | Q | 7 | T
3 | 4 | M | 5 | P | 6 | Q | 7 | R
3 | 4 | M | 5 | P | 6 | Q | 7 | S
3 | 4 | M | 5 | P | 6 | Q | 7 | T
3 | 4 | N | 5 | O | 6 | Q | 7 | R
3 | 4 | N | 5 | O | 6 | Q | 7 | S
3 | 4 | N | 5 | O | 6 | Q | 7 | T
3 | 4 | N | 5 | P | 6 | Q | 7 | R
3 | 4 | N | 5 | P | 6 | Q | 7 | S
3 | 4 | N | 5 | P | 6 | Q | 7 | T
DOUBT!
How can I make a single query that can give me the results independent of the number of different (distinct) groups, types, and elements?
To better understand the relationships between group, type, and element type columns:
Group -> Type[element A, element B ...]
1 -> 1[A,B], 2[C,D], 3[E,F]
2 -> 4[G,H], 5[I,J,K]
3 -> 4[L,M,N], 5[O,P], 6[Q], 7[R,S,T]

left join 2 tables not working

I have 2 tables:
Table1: 'op_ats'
| ID1 | numero |id_cofre | id_chave | estadoAT
| 1 | 111 | 1 | 3 | 1
| 2 | 222 | 3 | 3 | 2
| 3 | 333 | 1 | 4 | 2
| 4 | 444 | 1 | 2 | 3
Table_2: 'op_ats_cofres_chaves'
| ID2 | num_chave |
| 1 | A |
| 2 | B |
| 3 | C |
| 4 | D |
| 5 | E |
I have this SQL:
SELECT chaves.*, ats.numero numAT, ats.estadoAT
FROM op_ats_cofres_chaves chaves
LEFT JOIN op_ats ats ON ats.id_chave_cofre = chaves.id AND ats.id_cofre = 1
With this I get the following result:
| ID2 | num_chave | numAT | estadoAT |
| 1 | A | 444 | 3 |
| 2 | B | NULL | NULL |
| 3 | C | 111 | 1 |
| 4 | D | 333 | 2 |
| 5 | E | NULL | NULL |
Now the problem is that I want to filter the rows that are in Table1 but only that have the column 'estadoAT' with values 1 and 2. I've tried to add the line
WHERE op_ats.estadoAT = 1 OR op_ats.estadoAT = 2
But this makes the following result:
| ID2 | num_chave | numAT | estadoAT |
| 1 | A | 444 | 3 |
| 3 | C | 111 | 1 |
| 4 | D | 333 | 2 |
Resuming...
My intention is to get ALL rows in the Table2 and join the Table1 rows that have the 'id_cofre = 1' and '(estadoAT = 1 OR estadoAT = 2)'.
Any help is appreciated.
You have to move condition to JOIN clause instead of WHERE.
SELECT chaves.*, ats.numero numAT, ats.estadoAT
FROM op_ats_cofres_chaves chaves
LEFT JOIN op_ats ats ON ats.id_chave_cofre = chaves.id AND ats.id_cofre = 1
AND op_ats.estadoAT = 1 OR op_ats.estadoAT = 2;

Finding value difference in column pairs

I'm using SQL server 2008R2 and I have a view which returns the following:
+----+-------+-------+-------+-------+-------+-------+
| ID | col1A | col1B | col2A | col2B | col3A | col3B |
+----+-------+-------+-------+-------+-------+-------+
| 1 | 1 | 1 | 3 | 5 | 4 | 4 |
| 2 | 1 | 1 | 5 | 5 | 5 | 4 |
| 3 | 3 | 4 | 5 | 5 | 4 | 4 |
| 4 | 1 | 2 | 5 | 5 | 4 | 3 |
| 5 | 1 | 1 | 2 | 2 | 3 | 3 |
+----+-------+-------+-------+-------+-------+-------+
As you can see this view contains column pairs (col1A and col1B), (col2A and col2B), (col3A and col3B).
I need to query this view and find rows where the column pairs contain different values.
So I would be looking to return:
+----+------------+---+-----+
| ID | ColumnType | A | B |
+----+------------+---+-----+
| 1 | Col2 | 3 | 5 |
| 2 | Col3 | 5 | 4 |
| 3 | Col1 | 3 | 4 |
| 4 | Col1 | 1 | 2 |
| 4 | Col3 | 4 | 3 |
+----+------------+---+-----+
I think I need to use UNPIVOT but not sure how – appreciate any suggestions?
Since you are using SQL Server 2008+ you can use CROSS APPLY to unpivot the pair of columns and then you can easily compare the values in the A and B to return the rows that don't match:
select t.ID,
c.ColumnType,
c.A,
c.B
from [dbo].[yourview] t
cross apply
(
values
('Col1', Col1A, Col1B),
('Col2', Col2A, Col2B),
('Col3', Col3A, Col3B)
) c (ColumnType, A, B)
where c.A <> c.B;
If you have different datatypes in your columns, then you'll need to convert the data to the same type. You can do this conversion within the VALUES clause:
select t.ID,
c.ColumnType,
c.A,
c.B
from [dbo].[yourview] t
cross apply
(
values
('Col1', cast(Col1A as varchar(50)), Col1B),
('Col2', cast(Col2A as varchar(50)), Col2B),
('Col3', cast(Col3A as varchar(50)), Col3B)
) c (ColumnType, A, B)
where c.A <> c.B