Skip to content

Commit

Permalink
[expression] further improve replace() to support a map argument
Browse files Browse the repository at this point in the history
  • Loading branch information
nirvn committed Oct 27, 2016
1 parent 8b74201 commit 3fb2d9e
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 36 deletions.
24 changes: 16 additions & 8 deletions resources/function_help/json/replace
@@ -1,12 +1,20 @@
{
"name": "replace",
"type": "function",
"description": "Returns a string with the supplied string or array of strings replaced by a string or an array of strings.",
"arguments": [ {"arg":"string","description":"the input string"},
{"arg":"before","description":"the string or array of strings to replace"},
{"arg":"after","description":"the string or array of strings to use as a replacement"}],
"examples": [ { "expression":"replace('QGIS SHOULD ROCK','SHOULD','DOES')", "returns":"'QGIS DOES ROCK'"},
{ "expression":"replace('QGIS ABC',array('A','B','C'),array('X','Y','Z'))", "returns":"'QGIS XYZ'"},
{ "expression":"replace('QGIS',array('Q','S'),'')", "returns":"'GI'"}
]
"description": "Returns a string with the supplied string, array, or map of strings replaced.",
"variants": [
{ "variant": "String & array variant",
"variant_description": "Returns a string with the supplied string or array of strings replaced by a string or an array of strings.",
"arguments": [ {"arg":"string","description":"the input string"},
{"arg":"before","description":"the string or array of strings to replace"},
{"arg":"after","description":"the string or array of strings to use as a replacement"}],
"examples": [ { "expression":"replace('QGIS SHOULD ROCK','SHOULD','DOES')", "returns":"'QGIS DOES ROCK'"},
{ "expression":"replace('QGIS ABC',array('A','B','C'),array('X','Y','Z'))", "returns":"'QGIS XYZ'"},
{ "expression":"replace('QGIS',array('Q','S'),'')", "returns":"'GI'"} ] },
{ "variant": "Map variant",
"variant_description": "Returns a string with the supplied map keys replaced by paired values.",
"arguments": [ {"arg":"string","description":"the input string"},
{"arg":"map","description":"the map containing keys and values"} ],
"examples": [ { "expression":"replace('APP SHOULD ROCK',map('APP','QGIS','SHOULD','DOES'))", "returns":"'QGIS DOES ROCK'"} ]
}]
}
76 changes: 48 additions & 28 deletions src/core/qgsexpression.cpp
Expand Up @@ -1214,42 +1214,62 @@ static QVariant fcnLength( const QVariantList& values, const QgsExpressionContex

static QVariant fcnReplace( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
{
QString str = getStringValue( values.at( 0 ), parent );
QVariantList before;
QVariantList after;
bool isSingleReplacement = false;

if ( values.at( 1 ).type() != QVariant::List && values.at( 2 ).type() != QVariant::StringList )
if ( values.count() == 2 && values.at( 1 ).type() == QVariant::Map )
{
before = QVariantList() << getStringValue( values.at( 1 ), parent );
QString str = getStringValue( values.at( 0 ), parent );
QVariantMap map = getMapValue( values.at( 1 ), parent );

for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
{
str = str.replace( it.key(), it.value().toString() );
}

return QVariant( str );
}
else
else if ( values.count() == 3 )
{
before = getListValue( values.at( 1 ), parent );
}
QString str = getStringValue( values.at( 0 ), parent );
QVariantList before;
QVariantList after;
bool isSingleReplacement = false;

if ( values.at( 2 ).type() != QVariant::List && values.at( 2 ).type() != QVariant::StringList )
{
after = QVariantList() << getStringValue( values.at( 2 ), parent );
isSingleReplacement = true;
if ( values.at( 1 ).type() != QVariant::List && values.at( 2 ).type() != QVariant::StringList )
{
before = QVariantList() << getStringValue( values.at( 1 ), parent );
}
else
{
before = getListValue( values.at( 1 ), parent );
}

if ( values.at( 2 ).type() != QVariant::List && values.at( 2 ).type() != QVariant::StringList )
{
after = QVariantList() << getStringValue( values.at( 2 ), parent );
isSingleReplacement = true;
}
else
{
after = getListValue( values.at( 2 ), parent );
}

if ( !isSingleReplacement && before.length() != after.length() )
{
parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
return QVariant();
}

for ( int i = 0; i < before.length(); i++ )
{
str = str.replace( before.at( i ).toString(), after.at( isSingleReplacement ? 0 : i ).toString() );
}

return QVariant( str );
}
else
{
after = getListValue( values.at( 2 ), parent );
}

if ( !isSingleReplacement && before.length() != after.length() )
{
parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
parent->setEvalErrorString( QObject::tr( "Function replace requires 2 or 3 arguments" ) );
return QVariant();
}

for ( int i = 0; i < before.length(); i++ )
{
str = str.replace( before.at( i ).toString(), after.at( isSingleReplacement ? 0 : i ).toString() );
}

return QVariant( str );
}
static QVariant fcnRegexpReplace( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
{
Expand Down Expand Up @@ -3521,7 +3541,7 @@ const QList<QgsExpression::Function*>& QgsExpression::Functions()
<< new StaticFunction( QStringLiteral( "char" ), 1, fcnChar, QStringLiteral( "String" ) )
<< new StaticFunction( QStringLiteral( "wordwrap" ), ParameterList() << Parameter( QStringLiteral( "text" ) ) << Parameter( QStringLiteral( "length" ) ) << Parameter( QStringLiteral( "delimiter" ), true, "" ), fcnWordwrap, QStringLiteral( "String" ) )
<< new StaticFunction( QStringLiteral( "length" ), ParameterList() << Parameter( QStringLiteral( "text" ), true, "" ), fcnLength, QStringList() << QStringLiteral( "String" ) << QStringLiteral( "GeometryGroup" ) )
<< new StaticFunction( QStringLiteral( "replace" ), 3, fcnReplace, QStringLiteral( "String" ) )
<< new StaticFunction( QStringLiteral( "replace" ), -1, fcnReplace, QStringLiteral( "String" ) )
<< new StaticFunction( QStringLiteral( "regexp_replace" ), 3, fcnRegexpReplace, QStringLiteral( "String" ) )
<< new StaticFunction( QStringLiteral( "regexp_substr" ), 2, fcnRegexpSubstr, QStringLiteral( "String" ) )
<< new StaticFunction( QStringLiteral( "substr" ), 3, fcnSubstr, QStringLiteral( "String" ) )
Expand Down
1 change: 1 addition & 0 deletions tests/src/core/testqgsexpression.cpp
Expand Up @@ -825,6 +825,7 @@ class TestQgsExpression: public QObject
QTest::newRow( "replace (array replaced by string)" ) << "replace('12345', array('2','4'), '')" << false << QVariant( "135" );
QTest::newRow( "replace (unbalanced array, before > after)" ) << "replace('12345', array('1','2','3'), array('6','7'))" << true << QVariant();
QTest::newRow( "replace (unbalanced array, before < after)" ) << "replace('12345', array('1','2'), array('6','7','8'))" << true << QVariant();
QTest::newRow( "replace (map)" ) << "replace('APP SHOULD ROCK',map('APP','QGIS','SHOULD','DOES'))" << false << QVariant( "QGIS DOES ROCK" );
QTest::newRow( "regexp_replace" ) << "regexp_replace('HeLLo','[eL]+', '-')" << false << QVariant( "H-o" );
QTest::newRow( "regexp_replace greedy" ) << "regexp_replace('HeLLo','(?<=H).*L', '-')" << false << QVariant( "H-o" );
QTest::newRow( "regexp_replace non greedy" ) << "regexp_replace('HeLLo','(?<=H).*?L', '-')" << false << QVariant( "H-Lo" );
Expand Down

0 comments on commit 3fb2d9e

Please sign in to comment.