dart-lang / sdk

The Dart SDK, including the VM, JS and Wasm compilers, analysis, core libraries, and more.
https://dart.dev
BSD 3-Clause "New" or "Revised" License
10.21k stars 1.57k forks source link

I'm missing the (extension) functions isAnyOf(...) and isNoneOf(...) #55115

Open rekire opened 7 months ago

rekire commented 7 months ago

I have often the problem that I want to check if a value is one of a very limited list of options. In the past I wrote a extension function with a list of arguments which worked fine for me but was somehow award that this required a list. Now I found randomly a trick in Object.hash(...) which makes it almost perfect. Here is my suggestion for an extension which should be part of dart out of my view:

const Object _notSet = Object();

extension AnyNoneExtension on Object {
  bool isAnyOf(
    Object a,
    Object b, [
    Object c = _notSet,
    Object d = _notSet,
    Object e = _notSet,
    Object f = _notSet,
    Object g = _notSet,
    Object h = _notSet,
    Object i = _notSet,
    Object j = _notSet,
    Object k = _notSet,
    Object l = _notSet,
    Object m = _notSet,
    Object n = _notSet,
    Object o = _notSet,
    Object p = _notSet,
    Object q = _notSet,
    Object r = _notSet,
    Object s = _notSet,
    Object t = _notSet,
    Object u = _notSet,
    Object v = _notSet,
    Object w = _notSet,
    Object x = _notSet,
    Object y = _notSet,
    Object z = _notSet,
  ]) =>
      [
        a,
        b,
        if (c != _notSet) c,
        if (d != _notSet) d,
        if (e != _notSet) e,
        if (f != _notSet) f,
        if (g != _notSet) g,
        if (h != _notSet) h,
        if (i != _notSet) i,
        if (j != _notSet) j,
        if (k != _notSet) k,
        if (l != _notSet) l,
        if (m != _notSet) m,
        if (n != _notSet) n,
        if (o != _notSet) o,
        if (p != _notSet) p,
        if (q != _notSet) q,
        if (r != _notSet) r,
        if (s != _notSet) s,
        if (t != _notSet) t,
        if (u != _notSet) u,
        if (v != _notSet) v,
        if (w != _notSet) w,
        if (x != _notSet) x,
        if (y != _notSet) y,
        if (z != _notSet) z,
      ].contains(this);

  bool isNoneOf(
    Object a,
    Object b, [
    Object c = _notSet,
    Object d = _notSet,
    Object e = _notSet,
    Object f = _notSet,
    Object g = _notSet,
    Object h = _notSet,
    Object i = _notSet,
    Object j = _notSet,
    Object k = _notSet,
    Object l = _notSet,
    Object m = _notSet,
    Object n = _notSet,
    Object o = _notSet,
    Object p = _notSet,
    Object q = _notSet,
    Object r = _notSet,
    Object s = _notSet,
    Object t = _notSet,
    Object u = _notSet,
    Object v = _notSet,
    Object w = _notSet,
    Object x = _notSet,
    Object y = _notSet,
    Object z = _notSet,
  ]) =>
      !isAnyOf(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z);
}

Usage sample:

final weekday = currentWeekday();
if (weekday.isAnyOf('Monday', 'Sunday') && weekday.isNoneOf('Saturday', 'Friday')) {
  print('Do something fancy');
}

I'm curious what you think about it.

Wdestroier commented 7 months ago

Did you try const {'Monday', 'Sunday'}.contains(weekday)? It should be more performant.

main() {
  var weekday = 'Monday';
  print(const {'Monday', 'Tuesday'}.contains(weekday)); // true

  weekday = 'Sunday';
  print(const {'Monday', 'Tuesday'}.contains(weekday)); // false
}

You may want to move this request to the dart-lang/sdk repository.

lrhn commented 7 months ago

I'd probably use the constant set too, if the values can be constant, at least unless performance is very, very important, and I know something that can make the check more efficient than a hash set lookup (or a linear v == c1 || v == c2 || ... check).

I'd definitely not create a list, or even a set, for every call and then do a contains on that. Doing linear work to set up an efficient one-use contains is strictly worse than just v == v1 || v == v2 || ... || v == vn.

A more direct approach could be:

extension AnyOf on Object {
  bool anyOf(Object v1, Object v2,
      [Object? v3,
      Object? v4,
      Object? v5,
      Object? v6,
      Object? v7,
      Object? v8,
      Object? v9,
      Object? v10]) {
    return this == v1 ||
        this == v2 ||
        this == v3 ||
        v3 != null &&
            (this == v4 ||
                v4 != null &&
                    (this == v5 ||
                        v5 != null &&
                            (this == v6 ||
                                v6 != null &&
                                    (this == v7 ||
                                        v7 != null &&
                                            (this == v8 ||
                                                v8 != null &&
                                                    (this == v9 ||
                                                        v9 != null &&
                                                            (this == v10)))))));
  }
}
Wdestroier commented 7 months ago

I enjoy the performance improvement and the code may look more idiomatic. Reusing the code should be simple too. For example: by creating an isWeekend function. The major problem would be polluting the Object namespace I guess.