Skip to content

Commit 51ec333

Browse files
authored
Merge pull request #7688 from m-kuhn/array_filter
Add array_filter expression function
2 parents 0b96fd9 + d9e59e2 commit 51ec333

File tree

4 files changed

+136
-0
lines changed

4 files changed

+136
-0
lines changed
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"name": "array_filter",
3+
"type": "function",
4+
"description": "Returns an array with only the items for which the expression evaluates to true.",
5+
"arguments": [
6+
{"arg":"array","description":"an array"},
7+
{"arg":"expression","description":"an expression to evaluate on each item. The variable `@element` will be replaced by the current value."}
8+
],
9+
"examples": [
10+
{ "expression": "array_filter(array(1,2,3),@element < 3)", "returns":"array: 1, 2"}
11+
]
12+
}

src/core/expression/qgsexpressionfunction.cpp

+95
Original file line numberDiff line numberDiff line change
@@ -4730,6 +4730,7 @@ const QList<QgsExpressionFunction *> &QgsExpression::Functions()
47304730

47314731
// functions for arrays
47324732
<< new QgsArrayForeachExpressionFunction()
4733+
<< new QgsArrayFilterExpressionFunction()
47334734
<< new QgsStaticExpressionFunction( QStringLiteral( "array" ), -1, fcnArray, QStringLiteral( "Arrays" ), QString(), false, QSet<QString>(), false, QStringList(), true )
47344735
<< new QgsStaticExpressionFunction( QStringLiteral( "array_length" ), 1, fcnArrayLength, QStringLiteral( "Arrays" ) )
47354736
<< new QgsStaticExpressionFunction( QStringLiteral( "array_contains" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayContains, QStringLiteral( "Arrays" ) )
@@ -4869,6 +4870,100 @@ bool QgsArrayForeachExpressionFunction::prepare( const QgsExpressionNodeFunction
48694870
return true;
48704871
}
48714872

4873+
QgsArrayFilterExpressionFunction::QgsArrayFilterExpressionFunction()
4874+
: QgsExpressionFunction( QStringLiteral( "array_filter" ), QgsExpressionFunction::ParameterList()
4875+
<< QgsExpressionFunction::Parameter( QStringLiteral( "array" ) )
4876+
<< QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ),
4877+
QCoreApplication::tr( "Arrays" ) )
4878+
{
4879+
4880+
}
4881+
4882+
bool QgsArrayFilterExpressionFunction::isStatic( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) const
4883+
{
4884+
bool isStatic = false;
4885+
4886+
QgsExpressionNode::NodeList *args = node->args();
4887+
4888+
if ( args->count() < 2 )
4889+
return false;
4890+
4891+
if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
4892+
{
4893+
isStatic = true;
4894+
}
4895+
return isStatic;
4896+
}
4897+
4898+
QVariant QgsArrayFilterExpressionFunction::run( QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
4899+
{
4900+
Q_UNUSED( node )
4901+
QVariantList result;
4902+
4903+
if ( args->count() < 2 )
4904+
// error
4905+
return result;
4906+
4907+
const QVariantList array = args->at( 0 )->eval( parent, context ).toList();
4908+
4909+
QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
4910+
std::unique_ptr< QgsExpressionContext > tempContext;
4911+
if ( !subContext )
4912+
{
4913+
tempContext = qgis::make_unique< QgsExpressionContext >();
4914+
subContext = tempContext.get();
4915+
}
4916+
4917+
QgsExpressionContextScope *subScope = new QgsExpressionContextScope();
4918+
subContext->appendScope( subScope );
4919+
4920+
for ( const QVariant &value : array )
4921+
{
4922+
subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), value, true ) );
4923+
if ( args->at( 1 )->eval( parent, subContext ).toBool() )
4924+
result << value;
4925+
}
4926+
4927+
if ( context )
4928+
delete subContext->popScope();
4929+
4930+
return result;
4931+
}
4932+
4933+
QVariant QgsArrayFilterExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
4934+
{
4935+
// This is a dummy function, all the real handling is in run
4936+
Q_UNUSED( values )
4937+
Q_UNUSED( context )
4938+
Q_UNUSED( parent )
4939+
Q_UNUSED( node )
4940+
4941+
Q_ASSERT( false );
4942+
return QVariant();
4943+
}
4944+
4945+
bool QgsArrayFilterExpressionFunction::prepare( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) const
4946+
{
4947+
QgsExpressionNode::NodeList *args = node->args();
4948+
4949+
if ( args->count() < 2 )
4950+
// error
4951+
return false;
4952+
4953+
args->at( 0 )->prepare( parent, context );
4954+
4955+
QgsExpressionContext subContext;
4956+
if ( context )
4957+
subContext = *context;
4958+
4959+
QgsExpressionContextScope *subScope = new QgsExpressionContextScope();
4960+
subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), QVariant(), true ) );
4961+
subContext.appendScope( subScope );
4962+
4963+
args->at( 1 )->prepare( parent, &subContext );
4964+
4965+
return true;
4966+
}
48724967
QgsWithVariableExpressionFunction::QgsWithVariableExpressionFunction()
48734968
: QgsExpressionFunction( QStringLiteral( "with_variable" ), QgsExpressionFunction::ParameterList() <<
48744969
QgsExpressionFunction::Parameter( QStringLiteral( "name" ) )

src/core/expression/qgsexpressionfunction.h

+23
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,29 @@ class QgsArrayForeachExpressionFunction : public QgsExpressionFunction
521521

522522
};
523523

524+
/**
525+
* Handles the ``array_filter(array, expression)`` expression function.
526+
* It temporarily appends a new scope to the expression context.
527+
*
528+
* \ingroup core
529+
* \note Not available in Python bindings
530+
* \since QGIS 3.4
531+
*/
532+
class QgsArrayFilterExpressionFunction : public QgsExpressionFunction
533+
{
534+
public:
535+
QgsArrayFilterExpressionFunction();
536+
537+
bool isStatic( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) const override;
538+
539+
QVariant run( QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node ) override;
540+
541+
QVariant func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node ) override;
542+
543+
bool prepare( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) const override;
544+
545+
};
546+
524547
/**
525548
* Handles the ``with_variable(name, value, node)`` expression function.
526549
* It temporarily appends a new scope to the expression context for all nested

tests/src/core/testqgsexpression.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -2691,6 +2691,9 @@ class TestQgsExpression: public QObject
26912691
foreachExpected << QStringLiteral( "ABC" ) << QStringLiteral( "HELLO" );
26922692
QCOMPARE( QgsExpression( "array_foreach(array:=array('abc', 'hello'), expression:=upper(@element))" ).evaluate( &context ), QVariant( foreachExpected ) );
26932693

2694+
QVariantList filterExpected = QVariantList() << QStringLiteral( "A: a" ) << QStringLiteral( "A: d" );
2695+
QCOMPARE( QgsExpression( "array_filter(array:=array('A: a', 'B: b', 'C: c', 'A: d'), expression:=substr(@element, 1, 2) = 'A:')" ).evaluate( &context ), QVariant( filterExpected ) );
2696+
26942697
QCOMPARE( QgsExpression( "array_intersect(array('1', '2', '3', '4'), array('4', '0', '2', '5'))" ).evaluate( &context ), QVariant( true ) );
26952698
QCOMPARE( QgsExpression( "array_intersect(array('1', '2', '3', '4'), array('0', '5'))" ).evaluate( &context ), QVariant( false ) );
26962699

@@ -2774,6 +2777,9 @@ class TestQgsExpression: public QObject
27742777
foreachExpected << 10 << 20 << 40;
27752778
QCOMPARE( QgsExpression( "array_foreach(array(1, 2, 4), @element * 10)" ).evaluate( &context ), QVariant( foreachExpected ) );
27762779

2780+
QVariantList filterExpected = QVariantList() << 1 << 2;
2781+
QCOMPARE( QgsExpression( "array_filter(array(1, 2, 4), @element < 3)" ).evaluate( &context ), QVariant( filterExpected ) );
2782+
27772783
QgsExpression badArray( QStringLiteral( "array_get('not an array', 0)" ) );
27782784
QCOMPARE( badArray.evaluate( &context ), QVariant() );
27792785
QVERIFY( badArray.hasEvalError() );

0 commit comments

Comments
 (0)