Perl multidimensional table with headers

I am trying to implement a multi-page table with headers .

Here is an example for 2D:

< dimension1 > /\ 'column0' 'column1' dimension0 'row0' data00 data10 \/ 'row1' data01 data11 

Headers for rows and columns are text, and data is everything. I want to be able to do something like this (the syntax may be different, I start in Perl):

 my $table = new table(2); # 2 is the number of dimensions # the following line creates a new row/column if it didn't exist previously $table['row0']['column0'] = data00; $table['row0']['column1'] = data01; $table['row1']['column0'] = data10; $table['row1']['column1'] = data11; # the following line returns the headers of the specified dimension $table->headers(0); => ('row0', 'row1') 

First question: Is there something similar in CPAN? (before asking, I really looked for a considerable amount of time, and I did not find anything like it)


Second question: Here is my attempt, I know this is ugly and probably wrong. Does any Perl expert take care of revising my code?

 package table; sub new { my $class = shift; my $dimensions = shift; my $self = bless({}, $class); $self->{dimensions} = $dimensions; $self->{data} = []; $self->{headers} = []; return $self; } sub get_dimensions { my $self = shift; return $self->{dimensions}; } # This function creates a header or return its index if it already existed. # Headers are encoded as an array of hashes so that this is O(1) amortized. sub header { my $self = shift; my $dimension = shift; my $header = shift; my $headers = $self->{headers}[$dimension]; if(!defined($headers)) { $headers = $self->{headers}[$dimension] = {}; } if(!defined($headers->{$header})) { $headers->{$header} = scalar keys %$headers; } return $headers->{$header}; } # This function returns the list of headers. Because the headers are # stored as a hash (`header=>index`), I need to retrieve the keys # and sort them by value. sub get_headers { my $self = shift; my $dimension = shift; my $headers = $self->{headers}[$dimension]; return [sort { $headers->{$a} cmp $headers->{$b} } keys %$headers]; } # This last function stores/retrieves data from the table. sub data { my $self = shift; my $data = $self->{data}; my $dimensions = $self->{dimensions}; for(my $i = 0; $i < $dimensions-1; ++$i) { my $index = $self->header($i, shift); if(!defined($data->[$index])) { $data->[$index] = []; } $data = $data->[$index]; } my $index = $self->header($dimensions-1, shift); my $value = shift; if(defined($value)) { $data->[$index] = $value; } return $data->[$index]; } 
+6
source share
3 answers

You need a structure for the size table "N". I doubt that there is a CPAN module that can do this, because this is not a very common situation.

The problem is that the data structure is growing pretty fast, as well as complexity.

You can store an N-dimensional table in one list using a bit of math to convert an N-dimensional array into one dimension. Say that X represents the dimension of X, and X 'is the length of this dimension. For a two-dimensional table, you can get the value by doing the following:

 X * Y` + Y. 

For a three-dimensional table X, Y, Z, the answer will be:

 X * (Y' * Z') + Y * Z' + Z 

For a four-dimensional table W, X, Y, Z, the answer will be:

 W * (X' * Y' * Z') + X * (Y' + Z') + Y * Z' + Z' 

(I hope the math is correct).

Therefore, I can imagine such a structure for an N-dimensional table. It will include two different classes: one representing size information, and the other representing actual data (including all measurements).

  • Size (Grade)
    • Title (alphanumeric string)
    • Measurement Size (Integer)
  • N-table (class)
    • Dimension Array (Dimension Class Objects)
    • Array of data (alphanumeric strings)

You can get the number of measurements by looking at:

 my $numOfDimensions = scalar @{$ntable->{DIMENSIONS}}; 

And you can get the dimension header $x by looking at:

 my xDimensionHeading = $ntable->{DIMENSION}->[$x]->{HEADING}; 

And the size of this measurement, looking at:

 my xDimensionSize = $ntable->{DIMENSION}->[$x]->{SIZE}; 

Of course, you would do this with true object-oriented calls, not with empty links, but this gives you an idea of โ€‹โ€‹how the structure will work.

Now you need a way to convert a list of integers that will represent the location of the cell in the cell in a one-dimensional array, and you will have the ability to receive and retrieve your data.

Will this be what you are looking for?


EDIT

Next to it, but I actually change the size of the table a lot (I canโ€™t determine their size in advance), and if I realized that your solution is not suitable for this.

This adds a lot of complications ...

We need to throw out the size in the Dimension class. And we cannot use one dimensional array to store our data.

I hope you do not change the dimension of the table.

We could do something like this:

  • N-table (class)
    • Dimension Header List {DIMENSION} โ†’ []
    • List of data {DATA} โ†’ [] (This may be a link to other lists)

The {DATA} list is a link of lists depending on the depth of the table. For instance:

  my data_3D = $table_3D->{DATA}->[$x]->[$y]->[$z]; my data_2D = $table_2D->{DATA}->[$x]->[$y]; 

Number of dimensions scalar @{$table->{DIMENSION}} .

The question is how can I access the data in such a way that dimensional neutral. I could require 2, 3, 4 or more dimensions, and I need to somehow structure my address in order to pull it out.

We may have some kind of cycling mechanism. We get a list of coordinates in @coordinates , and then look at each coordinate. The latter will indicate the data. The rest will be just another reference to another array.

  my $data = pop @coordinates; #First Coordinate $data = $table->[$data]; #Could be data if 1D table, could be a reference foreach my $coordinate (@coordinates) { die qq(Not enough coordinates) if ref $data ne 'ARRAY'; $data = $data->[$coordinate]; #Could be data, could be a reference } # Cell value is in $data 

It may also be possible to build a list of coordinates and then evaluate it. Again completely untested:

  $coordinates = "[" . join ("]->[" => @coordinates . "]"; 

If there were three coordinates, that would be

  $coordinates = "[$x]->[$y]->[$z]"; 

I'm not sure how a 1-dimensional array will work ...

From there, you can create an instruction and use eval on it and get the data.

You must have several methods.

  • Set dimensions
  • Set cell
  • Get cell
  • Checking the table is complete (I have no idea how this will work.

This is more of a brain dump, but I think it might work. You do not have any given table parameters, and this can work for any N-dimensional table.

+2
source

You can use Text :: TabularDisplay for this. Here is a brief study that I did with your example.

 use strict; use warnings; use Text::TabularDisplay; my $t = Text::TabularDisplay->new(('', 'column0', 'column1')); $t->add('row0', 'data00', 'data10'); $t->add('row1', 'data01', 'data11'); print $t->render; 

shows:

 +------+---------+---------+ | | column0 | column1 | +------+---------+---------+ | row0 | data00 | data10 | | row1 | data01 | data11 | +------+---------+---------+ 

I am not sure if this is exactly what you were looking for. You need to come up with a heading, leaving the first column blank.

+1
source

Text :: A table may be useful here. The following is a simple example: you can play with the various options that the module provides to create something close that you describe.

 #!/usr/bin/perl use warnings; use strict; use Text::Table; my $inner_table = Text::Table->new(qw(column0 column1)); $inner_table->load( [ qw(row0 data00 data01) ], [ qw(row1 data10 data11) ], ); my $outer_table = Text::Table->new(' ', 'dimension1'); $outer_table->load( ['dimension0', $inner_table->stringify ], ); print $outer_table; 

Exit

  C: \ Temp> t
            dimension1
 dimension0 column0 column1
            row0 data00
            row1 data10 
0
source

Source: https://habr.com/ru/post/898134/


All Articles