protected function filter($rows, $condition)
    {
        $result = [];

        foreach ((array)$rows as $row) {
            $actual = isset($row[$condition['field']]) ? $row[$condition['field']] : null;

            switch ($condition['op']) {
                case Operator::EQUAL:
                    if ($actual == $condition['value']) {
                        $result[] = $row;
                    }
                    break;

                case Operator::NOT_EQUAL:
                    if ($actual != $condition['value']) {
                        $result[] = $row;
                    }
                    break;

                case Operator::GREATER_THAN:
                    if ($actual > $condition['value']) {
                        $result[] = $row;
                    }
                    break;

                case Operator::GREATER_OR_EQUAL:
                    if ($actual >= $condition['value']) {
                        $result[] = $row;
                    }
                    break;

                case Operator::LESS_THAN:
                    if ($actual < $condition['value']) {
                        $result[] = $row;
                    }
                    break;

                case Operator::LESS_OR_EQUAL:
                    if ($actual <= $condition['value']) {
                        $result[] = $row;
                    }
                    break;

                case Operator::CONTAINS:
                    if (strpos($actual, $condition['value']) !== false) {
                        $result[] = $row;
                    }
                    break;

                case Operator::STARTS_WITH:
                    if (preg_match('~^' . preg_quote($condition['value'], '~') . '~', $actual)) {
                        $result[] = $row;
                    }
                    break;

                case Operator::ENDS_WITH:
                    if (preg_match('~' . preg_quote($condition['value'], '$~') . '~', $actual)) {
                        $result[] = $row;
                    }
                    break;

                case Operator::MATCHES:
                    if (preg_match('~' . str_replace('~', '\~', $condition['value']) . '~', $actual)) {
                        $result[] = $row;
                    }
                    break;

                case Operator::IN:
                    if (in_array($actual, $condition['value'])) {
                        $result[] = $row;
                    }
                    break;

                default:
                    throw new InvalidOperatorException;
                    break;
            }
        }

        return $result;
    }