Skip to content

Commit 57dfe4a

Browse files
committed
Add templateString method to perform substitutions
1 parent 64d2bcd commit 57dfe4a

File tree

3 files changed

+66
-13
lines changed

3 files changed

+66
-13
lines changed

src/lib/index.js

+30
Original file line numberDiff line numberDiff line change
@@ -728,3 +728,33 @@ lib.numSeparate = function(value, separators, separatethousands) {
728728

729729
return x1 + x2;
730730
};
731+
732+
var TEMPLATE_STRING_REGEX = /%{([^\s%{}]*)}/g;
733+
var SIMPLE_PROPERTY_REGEX = /^\w*$/;
734+
735+
/*
736+
* Substitute values from an object into a string
737+
*
738+
* Examples:
739+
* Lib.templateString('name: %{trace}', {trace: 'asdf'}) --> 'name: asdf'
740+
* Lib.templateString('name: %{trace[0].name}', {trace: [{name: 'asdf'}]}) --> 'name: asdf'
741+
*
742+
* @param {string} input string containing %{...} template strings
743+
* @param {obj} data object containing substitution values
744+
*
745+
* @return {string} templated string
746+
*/
747+
748+
lib.templateString = function(string, obj) {
749+
// Not all that useful, but cache nestedProperty instantiation
750+
// just in case it speeds things up *slightly*:
751+
var getterCache = {};
752+
753+
return string.replace(TEMPLATE_STRING_REGEX, function(dummy, key) {
754+
if(SIMPLE_PROPERTY_REGEX.test(key)) {
755+
return obj[key] || '';
756+
}
757+
getterCache[key] = getterCache[key] || lib.nestedProperty(obj, key).get;
758+
return getterCache[key]() || '';
759+
});
760+
};

src/transforms/groupby.js

+10-13
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,11 @@ exports.attributes = {
3939
valType: 'string',
4040
description: [
4141
'Pattern by which grouped traces are named. If only one trace is present,',
42-
'defaults to the group name (`"%g"`), otherwise defaults to the group name',
43-
'with trace name (`"%g (%t)"`). Available escape sequences are `%g`, which',
44-
'inserts the group name, and `%t`, which inserts the trace name. If grouping',
42+
'defaults to the group name (`"%{group}"`), otherwise defaults to the group name',
43+
'with trace name (`"%{group} (%{trace})"`). Available escape sequences are `%{group}`, which',
44+
'inserts the group name, and `%{trace}`, which inserts the trace name. If grouping',
4545
'GDP data by country when more than one trace is present, for example, the',
46-
'default "%g (%t)" would return "Monaco (GDP per capita)".'
46+
'default "%{group} (%{trace})" would return "Monaco (GDP per capita)".'
4747
].join(' ')
4848
},
4949
groupnames: {
@@ -100,7 +100,7 @@ exports.attributes = {
100100
* @return {object} transformOut
101101
* copy of transformIn that contains attribute defaults
102102
*/
103-
exports.supplyDefaults = function(transformIn, traceOut, layout, traceIn) {
103+
exports.supplyDefaults = function(transformIn, traceOut, layout) {
104104
var i;
105105
var transformOut = {};
106106

@@ -113,7 +113,7 @@ exports.supplyDefaults = function(transformIn, traceOut, layout, traceIn) {
113113
if(!enabled) return transformOut;
114114

115115
coerce('groups');
116-
coerce('nameformat', layout._dataLength > 1 ? '%g (%t)' : '%g');
116+
coerce('nameformat', layout._dataLength > 1 ? '%{group} (%{trace})' : '%{group}');
117117

118118
var nameFormatIn = transformIn.groupnames;
119119
var nameFormatOut = transformOut.groupnames = [];
@@ -172,12 +172,6 @@ exports.transform = function(data, state) {
172172
return newData;
173173
};
174174

175-
function computeName(pattern, traceName, groupName) {
176-
return pattern.replace(/%g/g, groupName)
177-
.replace(/%t/g, traceName);
178-
}
179-
180-
181175
function transformOne(trace, state) {
182176
var i, j, k, attr, srcArray, groupName, newTrace, transforms, arrayLookup;
183177
var groupNameObj;
@@ -224,7 +218,10 @@ function transformOne(trace, state) {
224218
if(suppliedName) {
225219
newTrace.name = suppliedName;
226220
} else {
227-
newTrace.name = computeName(opts.nameformat, trace.name, groupName);
221+
newTrace.name = Lib.templateString(opts.nameformat, {
222+
trace: trace.name,
223+
group: groupName
224+
});
228225
}
229226

230227
// In order for groups to apply correctly to other transform data (e.g.

test/jasmine/tests/lib_test.js

+26
Original file line numberDiff line numberDiff line change
@@ -1728,6 +1728,32 @@ describe('Test lib.js:', function() {
17281728
});
17291729
});
17301730
});
1731+
1732+
describe('templateString', function() {
1733+
it('evaluates attributes', function() {
1734+
expect(Lib.templateString('foo %{bar}', {bar: 'baz'})).toEqual('foo baz');
1735+
});
1736+
1737+
it('evaluates nested properties', function() {
1738+
expect(Lib.templateString('foo %{bar.baz}', {bar: {baz: 'asdf'}})).toEqual('foo asdf');
1739+
});
1740+
1741+
it('evaluates array nested properties', function() {
1742+
expect(Lib.templateString('foo %{bar[0].baz}', {bar: [{baz: 'asdf'}]})).toEqual('foo asdf');
1743+
});
1744+
1745+
it('subtitutes multiple matches', function() {
1746+
expect(Lib.templateString('foo %{group} %{trace}', {group: 'asdf', trace: 'jkl;'})).toEqual('foo asdf jkl;');
1747+
});
1748+
1749+
it('replaces missing matches with empty string', function() {
1750+
expect(Lib.templateString('foo %{group} %{trace}', {})).toEqual('foo ');
1751+
});
1752+
1753+
it('replaces empty key with empty string', function() {
1754+
expect(Lib.templateString('foo %{} %{}', {})).toEqual('foo ');
1755+
});
1756+
});
17311757
});
17321758

17331759
describe('Queue', function() {

0 commit comments

Comments
 (0)