-
-
Notifications
You must be signed in to change notification settings - Fork 403
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add @stdlib/plot/table/unicode
#2407
base: develop
Are you sure you want to change the base?
Conversation
Signed-off-by: Snehil Shah <[email protected]>
Re: loose parsing. I'd just raise an error. If the number of headers is off, that is probably a user bug. When an object is missing a field, that is trickier, as could mean missing data. However, again, I'd raise an error here. A user should arguably be explicitly in terms of what value should represent missing data (e.g., Re: methods. I'd opt for closer parity with sparklines. Namely,
and I would add a And similar to sparklines, I'd make the table an event emitter with Re: Re: Re: One could argue that the same logic applies to |
For the row separator default, wouldn't an empty string make more sense? |
|
||
b.tic(); | ||
for ( i = 0; i < b.iterations; i++ ) { | ||
str = table.setData( data(), headers() ).render(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You definitely do not want to be generating fresh data and headers for every benchmark iteration. Otherwise, you confound results.
/** | ||
* Create a Unicode table. | ||
* | ||
* @module @stdlib/plot/table/unicode |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing example code.
|
||
// VARIABLES // | ||
|
||
var CHARACTER_LENGTH = 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is incorrect. Users should be able to provide emojis, etc, which may be comprised of multiple code points. Instead, you need to check for the number of grapheme clusters.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, why must a row separator only be one character? Couldn't I also want a pattern? E.g., -+-+-
, or something similar?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, why must a row separator only be one character? Couldn't I also want a pattern? E.g.,
-+-+-
, or something similar?
I wanted to generalize the arguments. We have column separators and borders as well. And as they are vertical, having them span multiple characters makes things more complex. For instance, how do we place the corners and joints if we have a 3-character long vertical border? Although it does make sense for horizontal lines, but I figured it was making the API design inconsistent and messy. For instance, borders
takes in a shorthand top-right-bottom-left. We would have to only allow the top and bottom properties to be able to span multiple characters (or grapheme clusters to be accurate).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Single grapheme clusters are fine to use across the board for now. And understood regarding the difficulty in supporting multiple visual characters for vertical borders/separators. While horizontal support for multiple grapheme clusters would be straightforward to add, we can wait until a user requests such a feature.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wait, now that I think again, maybe I was seeing it the wrong way. We can have multiple characters for a vertical line. Say if it's -*#
. We just print:
-
*
#
This way, we never have a "width/thickness" of a line to be more than a single grapheme cluster so placing corners and joints becomes straightforward.
We would have to reserve joints and corners to be a single grapheme cluster though for obvious reasons
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense. Thanks for circling back!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also now that we are allowing multiple character strings, I was wondering if we could change the shorthand properties ('a b c d'
) into an array (['a', 'b', 'c', 'd']
). This would allow the user to also have spaces as part of their line characters?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That also makes sense. Same thing: either a string or an array of strings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
...meaning I shouldn't have to do ['a']
; I should be able to both 'a'
and ['a']
and have them both result in the same thing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry. This is for the shorthand properties. Yeah, just supporting an array of strings seems reasonable.
if ( !isString( separator ) ) { | ||
throw new TypeError( format( 'invalid assignment. `%s` must be a string. Value: `%s`.', 'rowSeparator', separator ) ); | ||
} | ||
if ( separator === 'None' ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wouldn't make None
the sentinel.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean we should another value to denote None
(like null
or undefined
)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, or the empty string.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should not use undefined
as the sentinel.
* @throws {Error} output must be able to accommodate every column individually | ||
* @returns {Array<number>} list of column indices | ||
*/ | ||
function resolveWrapping() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than use a closure, I suggest either figuring out a way to move these to the parent scope or adding them as private methods on the table prototype. Otherwise, each time render
is invoked, these functions have to be allocated, etc, which will hurt perf.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can move it to the module scope, and just take in the private properties as arguments? Adding them to the table prototype can be avoided (I think?) as these functions are only used when rendering and nowhere else..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is fine, as well.
|
||
#### UnicodeTable.prototype.alignment | ||
|
||
Alignment of datum in cell. The value must be either `'right'`, `'left'` or `'center'`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alignment of datum in cell. The value must be either `'right'`, `'left'` or `'center'`. | |
Alignment of datum in cell. The value must be either `'right'`, `'left'`, or `'center'`. |
The project uses Oxford commas.
data = new Float64Array( 50 ); | ||
for ( i = 0; i < data.length; i++ ) { | ||
data[ i ] = randu() * 100.0; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use @stdlib/random/array/uniform
instead. That way you can avoid loops.
headers = new Float64Array( 5 ); | ||
for ( i = 0; i < headers.length; i++ ) { | ||
headers[ i ] = randu() * 100.0; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest just hardcoding a list of strings here. Generating random headers is unlikely in user code.
Without empty string:
With empty string:
Most of the prior art don't separate rows by default (dataframes in ipython or jupyter, or python tabulate), and I think it looks better too without the rows separated |
Should I also have an argument for
So, say the class isn't initialized with the data or headers, then we should raise an error if the user provides an array of alignments? Because in general, the alignments array should be of the same length as the number of columns?
Should we allow them to give headers if the data doesn't exist yet? (or raise an error) |
Re: Re: alignments without data/headers. No, it just needs to be consistent. As soon as we're provided something that conveys the number of columns, from that point forward, the table has a fixed number of columns and everything just needs to be consistent. Re: headers without data. Yes, that seems reasonable and is applicable in streaming contexts, where you know the headers beforehand and are still awaiting data to be pushed. Re: row separator and empty string. By empty string, I did not mean create a row separator using the empty string. I meant using the empty string as a sentinel to convey that no row separator should be rendered. |
Just to make sure I get you correctly, if there is no data or headers yet, and we set the alignment array for 5 columns. If the user now sets the data that depicts 9 columns, we raise an error, right?
Ah, understood, yes that makes sense. |
Correct. Can raise with something like "invalid argument. Expected %d columns, but received data having %d columns.". |
Towards #2067
Description
This pull request:
@stdlib/plot/table/unicode
Table options:
'right'
.'─ │ ─ │'
.1
.'│'
.'┌ ┐ ┘ └'
.'─'
.'┼ ┬ ┤ ┴ ├'
.0
.0
.FLOAT64_MAX
.FLOAT64_MAX
.'None'
.Table methods:
Related Issues
This pull request:
Questions
The current implementation allows for loose parsing. So, if the data is not exactly tabular, in some cases, it still tries to parse, adjust and make sense of the data.
These are the only two cases where this can be seen:
Parser will automatically ignore 'col3' and value of
this._headers
after parsing will be[ 'col1', 'col2' ]
.Parser will automatically fill the missing
col2
value withundefined
.Should we be raising an error instead?
Other
No.
Checklist
@stdlib-js/reviewers