Skip to content

Commit

Permalink
array_count, array_prioritize, array_replace added to expressions (#4…
Browse files Browse the repository at this point in the history
  • Loading branch information
domi4484 committed Feb 24, 2021
1 parent ccaca73 commit 0707d6f
Show file tree
Hide file tree
Showing 6 changed files with 175 additions and 0 deletions.
9 changes: 9 additions & 0 deletions resources/function_help/json/array_count
@@ -0,0 +1,9 @@
{
"name": "array_count",
"type": "function",
"groups": ["Arrays"],
"description": "Counts the number of occurrences of a given value in an array.",
"arguments": [ {"arg":"array","description":"an array"},
{"arg":"value","description":"the value to count"}],
"examples": [ { "expression":"array_count(array('a', 'b', 'c', 'b'), 'b')", "returns":"2"}]
}
12 changes: 12 additions & 0 deletions resources/function_help/json/array_prioritize
@@ -0,0 +1,12 @@
{
"name": "array_prioritize",
"type": "function",
"groups": ["Arrays"],
"description": "Returns an array sorted using the ordering specified in another array. Values which are present in the first array but are missing from the second array will be added to the end of the result.",
"arguments": [
{"arg":"array", "description":"an array"},
{"arg":"array_prioritize", "description":"an array with values ordered by priority" }],
"examples": [ { "expression":"array_prioritize(array(1, 8, 2, 5), array(5, 4, 2, 1, 3, 8))", "returns":"[ 5, 2, 1, 8 ]"},
{"expression":"array_prioritize(array(5, 4, 2, 1, 3, 8), array(1, 8, 6, 5))", "returns":"[ 1, 8, 5, 4, 2, 3 ]"}
]
}
21 changes: 21 additions & 0 deletions resources/function_help/json/array_replace
@@ -0,0 +1,21 @@
{
"name": "array_replace",
"type": "function",
"groups": ["Array"],
"description": "Returns an array with the supplied value, array, or map of values replaced.",
"variants": [
{ "variant": "Value & array variant",
"variant_description": "Returns an array with the supplied value or array of values replaced by another value or an array of values.",
"arguments": [ {"arg":"array","description":"the input array"},
{"arg":"before","description":"the value or array of values to replace"},
{"arg":"after","description":"the value or array of values to use as a replacement"}],
"examples": [ { "expression":"array_replace(array('QGIS','SHOULD','ROCK'),'SHOULD','DOES')", "returns":"[ 'QGIS', 'DOES', 'ROCK' ]"},
{ "expression":"array_replace(array(3,2,1),array(1,2,3),array(7,8,9))", "returns":"[ 9, 8, 7 ]"},
{ "expression":"array_replace(array('Q','G','I','S'),array('Q','S'),'-')", "returns":"[ '-', 'G', 'I', '-' ]"} ] },
{ "variant": "Map variant",
"variant_description": "Returns an array with the supplied map keys replaced by their paired values.",
"arguments": [ {"arg":"array","description":"the input array"},
{"arg":"map","description":"the map containing keys and values"} ],
"examples": [ { "expression":"array_replace(array('APP', 'SHOULD', 'ROCK'),map('APP','QGIS','SHOULD','DOES'))", "returns":"[ 'QGIS', 'DOES', 'ROCK' ]"} ]
}]
}
97 changes: 97 additions & 0 deletions src/core/expression/qgsexpressionfunction.cpp
Expand Up @@ -1440,6 +1440,7 @@ static QVariant fcnReplace( const QVariantList &values, const QgsExpressionConte
return QVariant();
}
}

static QVariant fcnRegexpReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
{
QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
Expand Down Expand Up @@ -5326,6 +5327,11 @@ static QVariant fcnArrayContains( const QVariantList &values, const QgsExpressio
return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).contains( values.at( 1 ) ) );
}

static QVariant fcnArrayCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
{
return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).count( values.at( 1 ) ) );
}

static QVariant fcnArrayAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
{
QVariantList listA = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
Expand Down Expand Up @@ -5588,6 +5594,94 @@ static QVariant fcnArrayRemoveAll( const QVariantList &values, const QgsExpressi
return convertToSameType( list, values.at( 0 ).type() );
}

static QVariant fcnArrayReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
{
if ( values.count() == 2 && values.at( 1 ).type() == QVariant::Map )
{
QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );

QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
{
int index = list.indexOf( it.key() );
while ( index >= 0 )
{
list.replace( index, it.value() );
index = list.indexOf( it.key() );
}
}

return convertToSameType( list, values.at( 0 ).type() );
}
else if ( values.count() == 3 )
{
QVariantList before;
QVariantList after;
bool isSingleReplacement = false;

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

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

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

QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
for ( int i = 0; i < before.length(); i++ )
{
int index = list.indexOf( before.at( i ) );
while ( index >= 0 )
{
list.replace( index, after.at( isSingleReplacement ? 0 : i ) );
index = list.indexOf( before.at( i ) );
}
}

return convertToSameType( list, values.at( 0 ).type() );
}
else
{
parent->setEvalErrorString( QObject::tr( "Function array_replace requires 2 or 3 arguments" ) );
return QVariant();
}
}

static QVariant fcnArrayPrioritize( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
{
QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
QVariantList list_new;

for ( const QVariant &cur : QgsExpressionUtils::getListValue( values.at( 1 ), parent ) )
{
while ( list.removeOne( cur ) )
{
list_new.append( cur );
}
}

list_new.append( list );

return convertToSameType( list_new, values.at( 0 ).type() );
}

static QVariant fcnArrayCat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
{
QVariantList list;
Expand Down Expand Up @@ -7149,6 +7243,7 @@ const QList<QgsExpressionFunction *> &QgsExpression::Functions()
<< new QgsStaticExpressionFunction( QStringLiteral( "array_sort" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "ascending" ), true, true ), fcnArraySort, QStringLiteral( "Arrays" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "array_length" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayLength, QStringLiteral( "Arrays" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "array_contains" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayContains, QStringLiteral( "Arrays" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "array_count" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayCount, QStringLiteral( "Arrays" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "array_all" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array_b" ) ), fcnArrayAll, QStringLiteral( "Arrays" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "array_find" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayFind, QStringLiteral( "Arrays" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "array_get" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ), fcnArrayGet, QStringLiteral( "Arrays" ) )
Expand All @@ -7166,6 +7261,8 @@ const QList<QgsExpressionFunction *> &QgsExpression::Functions()
<< new QgsStaticExpressionFunction( QStringLiteral( "array_insert" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayInsert, QStringLiteral( "Arrays" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "array_remove_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ), fcnArrayRemoveAt, QStringLiteral( "Arrays" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "array_remove_all" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayRemoveAll, QStringLiteral( "Arrays" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "array_replace" ), -1, fcnArrayReplace, QStringLiteral( "Arrays" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "array_prioritize" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array_prioritize" ) ), fcnArrayPrioritize, QStringLiteral( "Arrays" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "array_cat" ), -1, fcnArrayCat, QStringLiteral( "Arrays" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "array_slice" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "start_pos" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "end_pos" ) ), fcnArraySlice, QStringLiteral( "Arrays" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "array_reverse" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayReverse, QStringLiteral( "Arrays" ) )
Expand Down
22 changes: 22 additions & 0 deletions src/core/expression/qgsexpressionfunction.h
Expand Up @@ -371,6 +371,28 @@ class QgsStaticExpressionFunction : public QgsExpressionFunction
{
}

/**
* Static function for evaluation against a QgsExpressionContext, using an unnamed list of parameter values and list
* of groups.
*/
QgsStaticExpressionFunction( const QString &fnname,
int params,
FcnEval fcn,
const QStringList &groups,
const QString &helpText = QString(),
bool usesGeometry = false,
const QSet<QString> &referencedColumns = QSet<QString>(),
bool lazyEval = false,
const QStringList &aliases = QStringList(),
bool handlesNull = false )
: QgsExpressionFunction( fnname, params, groups, helpText, lazyEval, handlesNull )
, mFnc( fcn )
, mAliases( aliases )
, mUsesGeometry( usesGeometry )
, mReferencedColumns( referencedColumns )
{
}

/**
* Static function for evaluation against a QgsExpressionContext, using a named list of parameter values.
*/
Expand Down
14 changes: 14 additions & 0 deletions tests/src/core/testqgsexpression.cpp
Expand Up @@ -1372,6 +1372,12 @@ class TestQgsExpression: public QObject
QTest::newRow( "array_to_string fail passing non-array" ) << "array_to_string('non-array',',')" << true << QVariant();
QTest::newRow( "array_unique" ) << "array_to_string(array_distinct(array('hello','world','world','hello')))" << false << QVariant( "hello,world" );
QTest::newRow( "array_unique fail passing non-array" ) << "array_distinct('non-array')" << true << QVariant();
QTest::newRow( "array_replace" ) << "array_replace(array('H','e','L','L','o'), 'L', 'x')" << false << QVariant( QVariantList() << "H" << "e" << "x" << "x" << "o" );
QTest::newRow( "array_replace (array replaced by array)" ) << "array_replace(array(3,2,1), array(1,2,3), array(7,8,9))" << false << QVariant( QVariantList() << 9 << 8 << 7 );
QTest::newRow( "array_replace (arrayreplaced by string)" ) << "array_replace(array(1,2,3,4,5), array(2,4), '')" << false << QVariant( QVariantList() << 1 << "" << 3 << "" << 5 );
QTest::newRow( "array_replace (unbalanced array, before > after)" ) << "array_replace(array(1,2,3,4,5), array(1,2,3), array(6,7))" << true << QVariant();
QTest::newRow( "array_replace (unbalanced array, before < after)" ) << "array_replace(array(1,2,3,4,5), array(1,2), array(6,7,8))" << true << QVariant();
QTest::newRow( "array_replace (map)" ) << "array_replace(array('APP','SHOULD','ROCK'),map('APP','QGIS','SHOULD','DOES'))" << false << QVariant( QVariantList() << "QGIS" << "DOES" << "ROCK" );

//fuzzy matching
QTest::newRow( "levenshtein" ) << "levenshtein('kitten','sitting')" << false << QVariant( 3 );
Expand Down Expand Up @@ -3416,6 +3422,10 @@ class TestQgsExpression: public QObject
QCOMPARE( QgsExpression( "array_contains(\"strings\", 'two')" ).evaluate( &context ), QVariant( true ) );
QCOMPARE( QgsExpression( "array_contains(\"strings\", 'three')" ).evaluate( &context ), QVariant( false ) );

QCOMPARE( QgsExpression( "array_count(array(1,2,1), 1)" ).evaluate( &context ), QVariant( 2 ) );
QCOMPARE( QgsExpression( "array_count(array(1,2,1), 2)" ).evaluate( &context ), QVariant( 1 ) );
QCOMPARE( QgsExpression( "array_count(array(1,2,1), 3)" ).evaluate( &context ), QVariant( 0 ) );

QCOMPARE( QgsExpression( "array_find(\"strings\", 'two')" ).evaluate( &context ), QVariant( 1 ) );
QCOMPARE( QgsExpression( "array_find(\"strings\", 'three')" ).evaluate( &context ), QVariant( -1 ) );

Expand Down Expand Up @@ -3444,6 +3454,10 @@ class TestQgsExpression: public QObject
removeAllExpected << QStringLiteral( "a" ) << QStringLiteral( "b" ) << QStringLiteral( "d" );
QCOMPARE( QgsExpression( "array_remove_all(array('a', 'b', 'c', 'd', 'c'), 'c')" ).evaluate( &context ), QVariant( removeAllExpected ) );

QVariantList prioritizeExpected;
prioritizeExpected << 5 << 2 << 1 << 8;
QCOMPARE( QgsExpression( "array_prioritize(array(1, 8, 2, 5), array(5, 4, 2, 1, 3, 8))" ).evaluate( &context ), QVariant( prioritizeExpected ) );

QStringList concatExpected = array;
concatExpected << QStringLiteral( "a" ) << QStringLiteral( "b" ) << QStringLiteral( "c" );
QCOMPARE( QgsExpression( "array_cat(\"strings\", array('a', 'b'), array('c'))" ).evaluate( &context ), QVariant( concatExpected ) );
Expand Down

0 comments on commit 0707d6f

Please sign in to comment.