Skip to content

Commit

Permalink
Merge pull request #3670 from nirvn/expression_replace_array
Browse files Browse the repository at this point in the history
[FEATURE] array support for the replace() expression function
  • Loading branch information
nyalldawson authored Oct 27, 2016
2 parents 642e4a4 + 3fb2d9e commit 1d09538
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 11 deletions.
22 changes: 16 additions & 6 deletions resources/function_help/json/replace
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
{
"name": "replace",
"type": "function",
"description": "Returns a string with the the supplied string replaced.",
"arguments": [ {"arg":"string","description":"the input string"},
{"arg":"before","description":"the string to replace"},
{"arg":"after","description":"the string to use as a replacement"}],
"examples": [ { "expression":"replace('QGIS SHOULD ROCK','SHOULD','DOES')", "returns":"'QGIS DOES ROCK'"}
]
"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'"} ]
}]
}
62 changes: 57 additions & 5 deletions src/core/qgsexpression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1214,10 +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 );
QString before = getStringValue( values.at( 1 ), parent );
QString after = getStringValue( values.at( 2 ), parent );
return QVariant( str.replace( before, after ) );
if ( values.count() == 2 && values.at( 1 ).type() == QVariant::Map )
{
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 if ( values.count() == 3 )
{
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 )
{
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
{
parent->setEvalErrorString( QObject::tr( "Function replace requires 2 or 3 arguments" ) );
return QVariant();
}
}
static QVariant fcnRegexpReplace( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
{
Expand Down Expand Up @@ -3489,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
5 changes: 5 additions & 0 deletions tests/src/core/testqgsexpression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -821,6 +821,11 @@ class TestQgsExpression: public QObject
QTest::newRow( "upper" ) << "upper('HeLLo')" << false << QVariant( "HELLO" );
QTest::newRow( "length" ) << "length('HeLLo')" << false << QVariant( 5 );
QTest::newRow( "replace" ) << "replace('HeLLo', 'LL', 'xx')" << false << QVariant( "Hexxo" );
QTest::newRow( "replace (array replaced by array)" ) << "replace('321', array('1','2','3'), array('7','8','9'))" << false << QVariant( "987" );
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 1d09538

Please sign in to comment.