diff --git a/resources/function_help/json/replace b/resources/function_help/json/replace index 509b2e46261..0f0ff23dab2 100644 --- a/resources/function_help/json/replace +++ b/resources/function_help/json/replace @@ -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'"} ] + }] } diff --git a/src/core/qgsexpression.cpp b/src/core/qgsexpression.cpp index 4ba41dab4eb..2ca74e461c8 100644 --- a/src/core/qgsexpression.cpp +++ b/src/core/qgsexpression.cpp @@ -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 ) { @@ -3489,7 +3541,7 @@ const QList& 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" ) ) diff --git a/tests/src/core/testqgsexpression.cpp b/tests/src/core/testqgsexpression.cpp index 0d8d068c2c8..a35ae35a9e1 100644 --- a/tests/src/core/testqgsexpression.cpp +++ b/tests/src/core/testqgsexpression.cpp @@ -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" );