panzerdp / voca

The ultimate JavaScript string library
https://vocajs.pages.dev
MIT License
3.61k stars 137 forks source link

New way of string formatting #6

Closed SzymonStankiewicz closed 7 years ago

SzymonStankiewicz commented 7 years ago

Hello!

What do you think about idea of implementing such formatting?

If we would name this function format, then here are some examples of how it would work:

v.sformat('{}, {}, {}', 0, 1, 2); => '0, 1, 2' v.sformat('{0}, {0}, {1}, {2}, {3}', 1, 2, 3, 5); => '1, 1, 2, 3, 5' v.format('{name}: {a}+{b}={c}', {name: 'sum', a: 1, b: 2, c:3}); => 'sum: 1+2=3'

There would be also possibility to pass function to format.

If you think this is a good idea, then I would implement it in the coming days :smile:

panzerdp commented 7 years ago

Hi @Dakurels,

Most of the functionality that str.format() function provides is implemented by v.sprintf() function. I would like to avoid the duplication of functionality.
Additionally the ES2015 provides string literals that are powerful in formatting.

If you still have some interesting ideas how to extend v.sprintf() function, that may be an alternative. Thanks.

panzerdp commented 7 years ago

I like the idea about formatting an object by its properties (the 3rd example). A function specialized only on extracting object/array properties would be beneficial. A rough name for such function might be v.formatObject().

Some examples:

v.formatObject('{name}: {a}+{b}={c}', {name: 'sum', a: 1, b: 2, c:3}); 
// => 'sum: 1+2=3'
v.formatObject('{[0].name} is {[0].color}', [ { name: 'cloud', color: 'white' } ]) 
// => 'cloud is white'

I would like to implement this functionality out of the sprintf() function to keep the consistency. sprintf() directly uses its arguments to produce the formatted string, and does not query argument's object nested items (object properties or array elements).

@Dakurels What do you think about that? Thanks.

SzymonStankiewicz commented 7 years ago

Yeah, that's sounds great :smiley: I should create pull request in the next week :smirk:

panzerdp commented 7 years ago

Awesome. If you want to discuss any details, just let me know.

SzymonStankiewicz commented 7 years ago

I would limit this notation only to dots. E.g. [0].color -> 0.color colors[0].r -> colors.0.r users['alice'].email -> users.alice.email

Such approach is already in use (jinja templates for example) and parsing such syntax would be more straight forward.

What do you think about that?

panzerdp commented 7 years ago

I think we need to implement both cases. For a property like:

const obj = {
  'name.first': 'John',
  'name.last': 'Smith'
}
obj['name.first']

When the property name contains ., the square brackets syntax is necessary.

Another question is what to do when the accessed property does not exist. Should we evaluate that to an empty string, or throw an error. Maybe an empty string is more preferred.

SzymonStankiewicz commented 7 years ago

I still think, that idea of staying with only dots is better.

If we want to be able to get access to all possible properties then also such corner cases may appear:

const obj = {
  "']['.": 1
};

If we will use brackets or brackets with apostrophe as our syntax, then we have to escape brackets:

v.formatobject("{[']['.]}", obj); //=>obj["'"]["'."] or obj["[']['.]"] ?

So if we need to have a mechanism for escaping characters anyway, then why not to escape dots?

const obj = {
  'name.first': 'John',
  'name.last': 'Smith'
};
v.formatobject('{name\\.first}', obj); //=>'John'
panzerdp commented 7 years ago

The main idea is not to implement another type of selectors, like the proposed 0.color. I agree it feels lighter, but JavaScript by default uses [0] to access array elements.

This is the behavior that library's user expects to have, even without reading the documentation. And I want to keep the most expected way, not the shortest.

My final decision is to implement the property selectors exactly like in JavaScript. The only exception would be the [0].color, to access the element directly from an array.

The following syntax is allowed:

[0].color;
colors[0].r;
user['name'];
user["name"];
user.name;

And the following syntax is not allowed:

0.color;
colors.0.r;

The edge cases must be solved with escaping of the corresponding characters.

If you would like to implement this scenario, please let me know. If not, I will implement by self.

Anyway thanks for the productive discussion :+1:.

panzerdp commented 7 years ago

Probably the best for now is to pause the implementation of this function. In the end it just emulates the behavior of ES2015 template literals: The color is ${colors[0].r}.