diff --git a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK.C b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK.C
index b9f9e73ba230fe0e8d53b92b1023e433026c2e05..eb2514f37bc45e0734410f3053de2582b78f8dcb 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK.C
+++ b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK.C
@@ -172,10 +172,26 @@ labelList getSelectedPatches
     const wordRes& blacklist
 )
 {
-    DynamicList<label> patchIDs(patches.size());
+    // Name-based selection
+    labelList indices
+    (
+        stringListOps::findMatching
+        (
+            patches,
+            whitelist,
+            blacklist,
+            nameOp<polyPatch>()
+        )
+    );
+
+
+    // Remove undesirable patches
 
-    for (const polyPatch& pp : patches)
+    label count = 0;
+    for (const label patchi : indices)
     {
+        const polyPatch& pp = patches[patchi];
+
         if (isType<emptyPolyPatch>(pp))
         {
             continue;
@@ -185,33 +201,13 @@ labelList getSelectedPatches
             break; // No processor patches for parallel output
         }
 
-        const word& patchName = pp.name();
-
-        bool accept = false;
-
-        if (whitelist.size())
-        {
-            const auto matched = whitelist.matched(patchName);
-
-            accept =
-            (
-                matched == wordRe::LITERAL
-              ? true
-              : (matched == wordRe::REGEX && !blacklist.match(patchName))
-            );
-        }
-        else
-        {
-            accept = !blacklist.match(patchName);
-        }
-
-        if (accept)
-        {
-            patchIDs.append(pp.index());
-        }
+        indices[count] = patchi;
+        ++count;
     }
 
-    return patchIDs.shrink();
+    indices.resize(count);
+
+    return indices;
 }
 
 
diff --git a/applications/utilities/surface/surfaceMeshExtract/surfaceMeshExtract.C b/applications/utilities/surface/surfaceMeshExtract/surfaceMeshExtract.C
index ce6dc70ec4f4cc5dcdf977eaa0af214966696d92..2fff1bac66e9410bd2b6c9d52664252c9775a5ac 100644
--- a/applications/utilities/surface/surfaceMeshExtract/surfaceMeshExtract.C
+++ b/applications/utilities/surface/surfaceMeshExtract/surfaceMeshExtract.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2017-2019 OpenCFD Ltd.
+    Copyright (C) 2017-2020 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -67,10 +67,26 @@ labelList getSelectedPatches
     const wordRes& blacklist
 )
 {
-    DynamicList<label> patchIDs(patches.size());
+    // Name-based selection
+    labelList indices
+    (
+        stringListOps::findMatching
+        (
+            patches,
+            whitelist,
+            blacklist,
+            nameOp<polyPatch>()
+        )
+    );
+
+
+    // Remove undesirable patches
 
-    for (const polyPatch& pp : patches)
+    label count = 0;
+    for (const label patchi : indices)
     {
+        const polyPatch& pp = patches[patchi];
+
         if (isType<emptyPolyPatch>(pp))
         {
             continue;
@@ -80,33 +96,13 @@ labelList getSelectedPatches
             break; // No processor patches for parallel output
         }
 
-        const word& patchName = pp.name();
-
-        bool accept = false;
-
-        if (whitelist.size())
-        {
-            const auto matched = whitelist.matched(patchName);
-
-            accept =
-            (
-                matched == wordRe::LITERAL
-              ? true
-              : (matched == wordRe::REGEX && !blacklist.match(patchName))
-            );
-        }
-        else
-        {
-            accept = !blacklist.match(patchName);
-        }
-
-        if (accept)
-        {
-            patchIDs.append(pp.index());
-        }
+        indices[count] = patchi;
+        ++count;
     }
 
-    return patchIDs.shrink();
+    indices.resize(count);
+
+    return indices;
 }
 
 
diff --git a/src/OpenFOAM/primitives/strings/lists/stringListOps.H b/src/OpenFOAM/primitives/strings/lists/stringListOps.H
index 2eb5ac44273a4a32edd7c8d2dce841b03ac68a8e..9e60bbdd196dfc12155a09db3fd4058784d1d9aa 100644
--- a/src/OpenFOAM/primitives/strings/lists/stringListOps.H
+++ b/src/OpenFOAM/primitives/strings/lists/stringListOps.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2017-2018 OpenCFD Ltd.
+    Copyright (C) 2017-2020 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -47,6 +47,7 @@ SourceFiles
 #include "labelList.H"
 #include "stringList.H"
 #include "wordRes.H"
+#include "flipOp.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -346,6 +347,32 @@ struct foundOp
     }
 };
 
+
+//- Return ids for items with matching names.
+//  Uses a combination of whitelist and blacklist.
+//
+//  An empty whitelist accepts everything that is not blacklisted.
+//  A regex match is trumped by a literal match.
+//
+//  Eg,
+//  \verbatim
+//     input:  ( abc apple wall wall1 wall2 )
+//     whitelist:  ( abc  def  "wall.*" )
+//     blacklist:  ( "[ab].*"  wall )
+//
+//     result:  (abc wall1 wall2)
+//  \endverbatim
+//
+//  \return List indices for matches
+template<class StringListType, class AccessOp = noOp>
+labelList findMatching
+(
+    const StringListType& input,
+    const wordRes& whitelist,
+    const wordRes& blacklist = wordRes(),
+    AccessOp aop = noOp()
+);
+
 } // End namespace stringListOps
 
 
diff --git a/src/OpenFOAM/primitives/strings/lists/stringListOpsTemplates.C b/src/OpenFOAM/primitives/strings/lists/stringListOpsTemplates.C
index e78cbc847603082a4b6f0375ac4993819d97ec5b..4de82c97e895ef519625c666fe5999e36b45eeef 100644
--- a/src/OpenFOAM/primitives/strings/lists/stringListOpsTemplates.C
+++ b/src/OpenFOAM/primitives/strings/lists/stringListOpsTemplates.C
@@ -109,4 +109,57 @@ void Foam::inplaceSubsetMatchingStrings
 }
 
 
+template<class StringListType, class AccessOp>
+Foam::labelList Foam::stringListOps::findMatching
+(
+    const StringListType& input,
+    const wordRes& whitelist,
+    const wordRes& blacklist,
+    AccessOp aop
+)
+{
+    const label len = input.size();
+
+    if (whitelist.empty() && blacklist.empty())
+    {
+        return identity(len);
+    }
+
+    labelList indices(len);
+
+    label count = 0;
+    for (label i=0; i < len; ++i)
+    {
+        const std::string& text = aop(input[i]);
+
+        bool accept = false;
+
+        if (whitelist.size())
+        {
+            const auto result = whitelist.matched(text);
+
+            accept =
+            (
+                result == wordRe::LITERAL
+              ? true
+              : (result == wordRe::REGEX && !blacklist.match(text))
+            );
+        }
+        else
+        {
+            accept = !blacklist.match(text);
+        }
+
+        if (accept)
+        {
+            indices[count] = i;
+            ++count;
+        }
+    }
+    indices.resize(count);
+
+    return indices;
+}
+
+
 // ************************************************************************* //
diff --git a/src/fileFormats/ensight/mesh/ensightMesh.C b/src/fileFormats/ensight/mesh/ensightMesh.C
index 4dfdf835c37e880bd1a7f527ec5a9fdb0a1cf827..d897a9e1e41d5fb9e6a9d920b9f24b23e3847ff9 100644
--- a/src/fileFormats/ensight/mesh/ensightMesh.C
+++ b/src/fileFormats/ensight/mesh/ensightMesh.C
@@ -42,69 +42,6 @@ const Foam::label Foam::ensightMesh::internalZone = -1;
 namespace Foam
 {
 
-// Find matching ids based on whitelist, blacklist
-//
-// An empty whitelist accepts everything that is not blacklisted.
-// A regex match is trumped by a literal match.
-//
-// Eg,
-//     input:  ( abc apple wall wall1 wall2 )
-//     whitelist:  ( abc  def  "wall.*" )
-//     blacklist:  ( "[ab].*"  wall )
-//
-//     result:  (abc wall1 wall2)
-//
-static labelList getSelected
-(
-    const UList<word>& input,
-    const wordRes& whitelist,
-    const wordRes& blacklist
-)
-{
-    const label len = input.size();
-
-    if (whitelist.empty() && blacklist.empty())
-    {
-        return identity(len);
-    }
-
-    labelList indices(len);
-
-    label count = 0;
-    for (label i=0; i < len; ++i)
-    {
-        const auto& text = input[i];
-
-        bool accept = false;
-
-        if (whitelist.size())
-        {
-            const auto result = whitelist.matched(text);
-
-            accept =
-            (
-                result == wordRe::LITERAL
-              ? true
-              : (result == wordRe::REGEX && !blacklist.match(text))
-            );
-        }
-        else
-        {
-            accept = !blacklist.match(text);
-        }
-
-        if (accept)
-        {
-            indices[count] = i;
-            ++count;
-        }
-    }
-    indices.resize(count);
-
-    return indices;
-}
-
-
 // Patch names without processor patches
 static wordList nonProcessorPatchNames(const polyBoundaryMesh& bmesh)
 {
@@ -237,7 +174,7 @@ void Foam::ensightMesh::correct()
     const labelList patchIds =
     (
         option().useBoundaryMesh()
-      ? getSelected
+      ? stringListOps::findMatching
         (
             patchNames,
             option().patchSelection(),
diff --git a/src/functionObjects/field/mapFields/mapFieldsTemplates.C b/src/functionObjects/field/mapFields/mapFieldsTemplates.C
index 4e50264f4463b42a8a0222d09eea915f9a628c28..dc89a68befcf60a68fb0b67d9f126074d4852e06 100644
--- a/src/functionObjects/field/mapFields/mapFieldsTemplates.C
+++ b/src/functionObjects/field/mapFields/mapFieldsTemplates.C
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2016-2019 OpenCFD Ltd.
+    Copyright (C) 2016-2020 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -124,10 +124,13 @@ bool Foam::functionObjects::mapFields::mapFieldType() const
     const fvMesh& mapRegion = mapRegionPtr_();
 
     wordList fieldNames(this->mesh_.names(VolFieldType::typeName));
-    const labelList selected = findStrings(fieldNames_, fieldNames);
+
+    const labelList selected(fieldNames_.matching(fieldNames));
+
     for (const label fieldi : selected)
     {
         const word& fieldName = fieldNames[fieldi];
+
         const VolFieldType& field = lookupObject<VolFieldType>(fieldName);
 
         if (!mapRegion.foundObject<VolFieldType>(fieldName))
@@ -160,7 +163,7 @@ bool Foam::functionObjects::mapFields::mapFieldType() const
         evaluateConstraintTypes(mappedField);
     }
 
-    return selected.size() > 0;
+    return !selected.empty();
 }
 
 
@@ -172,7 +175,9 @@ bool Foam::functionObjects::mapFields::writeFieldType() const
     const fvMesh& mapRegion = mapRegionPtr_();
 
     wordList fieldNames(this->mesh_.names(VolFieldType::typeName));
-    const labelList selected = findStrings(fieldNames_, fieldNames);
+
+    const labelList selected(fieldNames_.matching(fieldNames));
+
     for (const label fieldi : selected)
     {
         const word& fieldName = fieldNames[fieldi];
@@ -185,7 +190,7 @@ bool Foam::functionObjects::mapFields::writeFieldType() const
         Log << "    " << fieldName << ": written";
     }
 
-    return selected.size() > 0;
+    return !selected.empty();
 }
 
 
diff --git a/src/functionObjects/field/regionSizeDistribution/regionSizeDistribution.C b/src/functionObjects/field/regionSizeDistribution/regionSizeDistribution.C
index 935a9470b52e7dbb60b927f893afb663dcfa78aa..ff96d1f24f6a2b102157e4c82ad3ddd1e6912e1d 100644
--- a/src/functionObjects/field/regionSizeDistribution/regionSizeDistribution.C
+++ b/src/functionObjects/field/regionSizeDistribution/regionSizeDistribution.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2013-2016 OpenFOAM Foundation
-    Copyright (C) 2016-2019 OpenCFD Ltd.
+    Copyright (C) 2016-2020 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -865,11 +865,13 @@ bool Foam::functionObjects::regionSizeDistribution::write()
         // Collect some more field
         {
             wordList scalarNames(obr_.names(volScalarField::typeName));
-            labelList selected = findStrings(fields_, scalarNames);
 
-            forAll(selected, i)
+            const labelList selected(fields_.matching(scalarNames));
+
+            for (const label fieldi : selected)
             {
-                const word& fldName = scalarNames[selected[i]];
+                const word& fldName = scalarNames[fieldi];
+
                 Log << "    Scalar field " << fldName << endl;
 
                 const scalarField& fld = obr_.lookupObject
@@ -894,11 +896,13 @@ bool Foam::functionObjects::regionSizeDistribution::write()
         }
         {
             wordList vectorNames(obr_.names(volVectorField::typeName));
-            labelList selected = findStrings(fields_, vectorNames);
 
-            forAll(selected, i)
+            const labelList selected(fields_.matching(vectorNames));
+
+            for (const label fieldi : selected)
             {
-                const word& fldName = vectorNames[selected[i]];
+                const word& fldName = vectorNames[fieldi];
+
                 Log << "    Vector field " << fldName << endl;
 
                 vectorField fld = obr_.lookupObject