Repository: DHI/mikeio
Pull request #383 (2022-06-28 20:35:36)

Filter items with wildcard
Example with dfs0 but works with any dfs file
![image](https://user-images.githubusercontent.com/614215/176280312-6f620674-1f24-4b1e-92ec-8cd51c5dec08.png)



Patch:
diff --git a/mikeio/__init__.py b/mikeio/__init__.py
index eaf87e94..c0d4dbbe 100644
--- a/mikeio/__init__.py
+++ b/mikeio/__init__.py
@@ -88,6 +88,7 @@ def read(
     >>> ds = mikeio.read("ts.dfs0")
     >>> ds = mikeio.read("ts.dfs0", items=0)
     >>> ds = mikeio.read("ts.dfs0", items="Temperature")
+    >>> ds = mikeio.read("sw_points.dfs0, items="*Buoy 4*")
     >>> ds = mikeio.read("ts.dfs0", items=["u","v"], time="2016")
     >>> ds = mikeio.read("tide.dfs1", time="2018-5")
     >>> ds = mikeio.read("tide.dfs1", time=slice("2018-5-1","2018-6-1"))
diff --git a/mikeio/dataset.py b/mikeio/dataset.py
index 061fea05..8965b32b 100644
--- a/mikeio/dataset.py
+++ b/mikeio/dataset.py
@@ -725,7 +725,17 @@ class Dataset(DataUtilsMixin, TimeSeries, collections.abc.MutableMapping):
         # select items
         key = self._key_to_str(key)
         if isinstance(key, str):
-            return self._data_vars[key]
+            if "*" in key:
+                import fnmatch
+
+                data_vars = {
+                    k: da
+                    for k, da in self._data_vars.items()
+                    if fnmatch.fnmatch(k, key)
+                }
+                return Dataset(data=data_vars, validate=False)
+            else:
+                return self._data_vars[key]
 
         if isinstance(key, Iterable):
             data_vars = {}
diff --git a/mikeio/dfsutil.py b/mikeio/dfsutil.py
index 2d8a8967..fb869ef4 100644
--- a/mikeio/dfsutil.py
+++ b/mikeio/dfsutil.py
@@ -1,5 +1,4 @@
 from datetime import datetime
-from multiprocessing.sharedctypes import Value
 from typing import Iterable, List, Tuple, Union
 import numpy as np
 import pandas as pd
@@ -9,9 +8,19 @@ from .custom_exceptions import ItemsError
 from mikecore.DfsFile import DfsDynamicItemInfo, DfsFileInfo
 
 
+def _fuzzy_item_search(dfsItemInfo: List[DfsDynamicItemInfo], search: str):
+    import fnmatch
+
+    names = [info.Name for info in dfsItemInfo]
+    item_numbers = [i for i, name in enumerate(names) if fnmatch.fnmatch(name, search)]
+    if len(item_numbers) == 0:
+        raise KeyError(f"No items like: {search} found. Valid names are {names}")
+    return item_numbers
+
+
 def _valid_item_numbers(
     dfsItemInfo: List[DfsDynamicItemInfo],
-    items: Union[int, List[int], List[str]] = None,
+    items: Union[str, int, List[int], List[str]] = None,
     ignore_first: bool = False,
 ) -> Iterable[int]:
     start_idx = 1 if ignore_first else 0
@@ -20,7 +29,10 @@ def _valid_item_numbers(
         return list(range(n_items_file))
 
     if np.isscalar(items):
-        items = [items]
+        if isinstance(items, str) and "*" in items:
+            return _fuzzy_item_search(dfsItemInfo=dfsItemInfo, search=items)
+        else:
+            items = [items]
 
     for idx, item in enumerate(items):
         if isinstance(item, str):


Test patch:
diff --git a/tests/test_consistency.py b/tests/test_consistency.py
index 40451142..ae7c49a6 100644
--- a/tests/test_consistency.py
+++ b/tests/test_consistency.py
@@ -469,3 +469,44 @@ def test_read_dfs_time_list_int():
         dsgetitem = ds[0][time]
         assert all(dsr[0].time == dsgetitem.time)
         assert dsr[0].shape == dsgetitem.shape
+
+
+def test_filter_items_dfs0():
+
+    ds = mikeio.read("tests/testdata/sw_points.dfs0", items="*Point 42*")
+    assert ds.n_items == 15
+
+    for da in ds:
+        assert "Point 42" in da.name
+
+    ds = mikeio.read("tests/testdata/sw_points.dfs0", items="*Height*")
+    assert ds.n_items == 12
+
+    for da in ds:
+        assert "Height" in da.name
+
+    with pytest.raises(KeyError):
+        mikeio.read(
+            "tests/testdata/sw_points.dfs0", items="height*"
+        )  # Note missing wildcard in beginning
+
+
+def test_filter_items_wildcard_getitem():
+    dsall = mikeio.read("tests/testdata/sw_points.dfs0")
+
+    ds = dsall["*Height*"]
+    assert ds.n_items == 12
+
+
+def test_filter_items_dfsu():
+
+    ds = mikeio.read("tests/testdata/wind_north_sea.dfsu", items="*direction*")
+    assert ds.n_items == 1
+
+
+def test_filter_items_dfsu_getitem():
+
+    dsall = mikeio.read("tests/testdata/wind_north_sea.dfsu")
+    ds = dsall["*direction*"]
+    assert ds.n_items == 1
+    assert "direction" in ds[0].name