filter (filter) {
        if (!arguments.length) {
            return this._filters.length > 0 ? this._filters[0] : null;
        }
        let filters = this._filters;
        if (filter instanceof Array && filter[0] instanceof Array && !filter.isFiltered) {
            // toggle each filter
            filter[0].forEach(f => {
                if (this._hasFilterHandler(filters, f)) {
                    filters = this._removeFilterHandler(filters, f);
                } else {
                    filters = this._addFilterHandler(filters, f);
                }
            });
        } else if (filter === null) {
            filters = this._resetFilterHandler(filters);
        } else {
            if (this._hasFilterHandler(filters, filter)) {
                filters = this._removeFilterHandler(filters, filter);
            } else {
                filters = this._addFilterHandler(filters, filter);
            }
        }
        this._filters = this.applyFilters(filters);
        this._invokeFilteredListener(filter);

        if (this._root !== null && this.hasFilter()) {
            this.turnOnControls();
        } else {
            this.turnOffControls();
        }

        return this;
    }
Filter the chart by the given parameter, or return the current filter if no input parameter
is given.

The filter parameter can take one of these forms:
* A single value: the value will be toggled (added if it is not present in the current
filters, removed if it is present)
* An array containing a single array of values (`[[value,value,value]]`): each value is
toggled
* When appropriate for the chart, a {@link filters dc filter object} such as
  * {@link filters.RangedFilter `filters.RangedFilter`} for the
{@link CoordinateGridMixin CoordinateGridMixin} charts
  * {@link filters.TwoDimensionalFilter `filters.TwoDimensionalFilter`} for the
{@link HeatMap heat map}
  * {@link filters.RangedTwoDimensionalFilter `filters.RangedTwoDimensionalFilter`}
for the {@link ScatterPlot scatter plot}
* `null`: the filter will be reset using the
{@link BaseMixin#resetFilterHandler resetFilterHandler}

Note that this is always a toggle (even when it doesn't make sense for the filter type). If
you wish to replace the current filter, either call `chart.filter(null)` first - or it's more
efficient to call {@link BaseMixin#replaceFilter `chart.replaceFilter(filter)`} instead.

Each toggle is executed by checking if the value is already present using the
{@link BaseMixin#hasFilterHandler hasFilterHandler}; if it is not present, it is added
using the {@link BaseMixin#addFilterHandler addFilterHandler}; if it is already present,
it is removed using the {@link BaseMixin#removeFilterHandler removeFilterHandler}.

Once the filters array has been updated, the filters are applied to the
crossfilter dimension, using the {@link BaseMixin#filterHandler filterHandler}.

Once you have set the filters, call {@link BaseMixin#redrawGroup `chart.redrawGroup()`}
(or {@link redrawAll `redrawAll()`}) to redraw the chart's group.
@see {@link BaseMixin#addFilterHandler addFilterHandler}
@see {@link BaseMixin#removeFilterHandler removeFilterHandler}
@see {@link BaseMixin#resetFilterHandler resetFilterHandler}
@see {@link BaseMixin#filterHandler filterHandler}
@example
// filter by a single string
chart.filter('Sunday');
// filter by a single age
chart.filter(18);
// filter by a set of states
chart.filter([['MA', 'TX', 'ND', 'WA']]);
// filter by range -- note the use of filters.RangedFilter, which is different
// from the syntax for filtering a crossfilter dimension directly, dimension.filter([15,20])
chart.filter(filters.RangedFilter(15,20));
@param {*} [filter]
@returns {BaseMixin}