Skip to main content

Overview

Array operators enable checking if a value exists in a collection. Rule Engine JS provides two array membership operators:

in

Check if value exists in array

notIn

Check if value does NOT exist in array

Architecture

Array operators are implemented in the ArrayOperators class extending BaseOperator: Source Files:
  • Array operators: src/operators/array.js
  • Base operator: src/operators/base/BaseOperator.js
  • Type utilities: src/utils/TypeUtils.js
  • Unit tests: tests/unit/operators/array.test.js

Key Features

  • Dynamic arrays - Array can be a field path or literal value
  • Dynamic values - Value to check can also be a field path
  • Strict/loose comparison - Control type coercion behavior
  • Case sensitive - Exact matching for strings by default
  • Comprehensive validation - Validates right operand is an array

in - Array Membership

Check if a value exists in an array.

Syntax

{ in: [value, array] }
{ in: [value, array, options] }

Parameters

value
any
required
The value to search for - field path or literal value
array
array
required
The array to search in - field path or literal array
options
object
Configuration options

Returns

boolean - true if value exists in array, false otherwise

Examples

import { createRuleEngine } from 'rule-engine-js';

const engine = createRuleEngine();
const user = {
  role: 'admin',
  tags: ['premium', 'verified', 'admin'],
  permissions: ['read', 'write', 'delete']
};

// Check if role is in allowed list
engine.evaluateExpr(
  { in: ['role', ['admin', 'moderator', 'user']] },
  user
);
// Result: { success: true }

// Check if tag exists
engine.evaluateExpr(
  { in: ['premium', 'tags'] },
  user
);
// Result: { success: true }

// Check permission
engine.evaluateExpr(
  { in: ['write', 'permissions'] },
  user
);
// Result: { success: true }

// Value not in array
engine.evaluateExpr(
  { in: ['role', ['guest', 'viewer']] },
  user
);
// Result: { success: false }

Common Use Cases

const user = {
  role: 'editor',
  department: 'marketing'
};

// Allow multiple roles
const canAccessAdmin = {
  in: ['role', ['admin', 'superadmin', 'owner']]
};

// Department-based access
const canAccessReports = {
  in: ['department', ['marketing', 'sales', 'analytics']]
};

engine.evaluateExpr(canAccessAdmin, user);
// Result: { success: false }

engine.evaluateExpr(canAccessReports, user);
// Result: { success: true }
const user = {
  id: 'user123',
  permissions: ['posts:read', 'posts:write', 'comments:read']
};

// Check specific permission
const canWritePosts = {
  in: ['posts:write', 'permissions']
};

// Check multiple permissions
const canManagePosts = {
  and: [
    { in: ['posts:read', 'permissions'] },
    { in: ['posts:write', 'permissions'] },
    { in: ['posts:delete', 'permissions'] }  // This will fail
  ]
};

engine.evaluateExpr(canWritePosts, user);
// Result: { success: true }

engine.evaluateExpr(canManagePosts, user);
// Result: { success: false } - missing delete permission
const user = {
  email: 'beta@company.com',
  features: ['dark-mode', 'new-editor', 'beta-search']
};

// Check if feature is enabled
const hasNewEditor = {
  in: ['new-editor', 'features']
};

// Multiple feature check
const hasBetaAccess = {
  or: [
    { in: ['beta-search', 'features'] },
    { in: ['beta-analytics', 'features'] }
  ]
};

engine.evaluateExpr(hasNewEditor, user);
// Result: { success: true }

engine.evaluateExpr(hasBetaAccess, user);
// Result: { success: true }
const article = {
  title: 'Introduction to React',
  tags: ['react', 'javascript', 'frontend', 'tutorial']
};

// Search by tag
const isReactArticle = {
  in: ['react', 'tags']
};

// Multiple tag filter
const isFrontendTutorial = {
  and: [
    { in: ['frontend', 'tags'] },
    { in: ['tutorial', 'tags'] }
  ]
};

engine.evaluateExpr(isReactArticle, article);
// Result: { success: true }

engine.evaluateExpr(isFrontendTutorial, article);
// Result: { success: true }
const order = {
  status: 'processing',
  validStatuses: ['pending', 'processing', 'shipped', 'delivered']
};

// Validate status is in allowed list
const validStatus = {
  in: ['status', 'validStatuses']
};

engine.evaluateExpr(validStatus, order);
// Result: { success: true }

// Check for specific statuses
const canCancel = {
  in: ['status', ['pending', 'processing']]
};

engine.evaluateExpr(canCancel, order);
// Result: { success: true }
Performance: Array membership uses optimized Array.some() with early exit for better performance.

notIn - Array Exclusion

Check if a value does NOT exist in an array (inverse of in).

Syntax

{ notIn: [value, array] }
{ notIn: [value, array, options] }

Parameters

value
any
required
The value to check for exclusion - field path or literal value
array
array
required
The array to check against - field path or literal array
options
object
Configuration options

Returns

boolean - true if value does NOT exist in array, false if it exists

Examples

const user = {
  role: 'admin',
  status: 'active',
  tags: ['premium', 'verified']
};

// Ensure role is not in banned list
engine.evaluateExpr(
  { notIn: ['role', ['guest', 'banned', 'suspended']] },
  user
);
// Result: { success: true }

// Check tag is not present
engine.evaluateExpr(
  { notIn: ['spam', 'tags'] },
  user
);
// Result: { success: true }

// This will fail (admin is in the list)
engine.evaluateExpr(
  { notIn: ['role', ['admin', 'moderator']] },
  user
);
// Result: { success: false }

Common Use Cases

const user = {
  email: 'user@example.com',
  ip: '192.168.1.100',
  blockedEmails: ['spam@test.com', 'fake@test.com'],
  blockedIPs: ['10.0.0.1', '192.168.1.50']
};

// Ensure user is not blacklisted
const notBlacklisted = {
  and: [
    { notIn: ['email', 'blockedEmails'] },
    { notIn: ['ip', 'blockedIPs'] }
  ]
};

engine.evaluateExpr(notBlacklisted, user);
// Result: { success: true }
const order = {
  status: 'processing',
  completedStatuses: ['shipped', 'delivered', 'cancelled']
};

// Can still modify order (not in completed statuses)
const canModify = {
  notIn: ['status', 'completedStatuses']
};

engine.evaluateExpr(canModify, order);
// Result: { success: true }

// Order not in these states
const notFinal = {
  notIn: ['status', ['cancelled', 'refunded']]
};

engine.evaluateExpr(notFinal, order);
// Result: { success: true }
const device = {
  platform: 'ios',
  version: '15.0',
  unsupportedPlatforms: ['windows-phone', 'blackberry'],
  deprecatedVersions: ['14.0', '14.1', '14.2']
};

// Check platform is supported
const isSupported = {
  and: [
    { notIn: ['platform', 'unsupportedPlatforms'] },
    { notIn: ['version', 'deprecatedVersions'] }
  ]
};

engine.evaluateExpr(isSupported, device);
// Result: { success: true }
const upload = {
  filename: 'document.pdf',
  extension: '.pdf',
  blockedExtensions: ['.exe', '.bat', '.sh', '.cmd']
};

// Ensure file type is allowed
const safeFileType = {
  notIn: ['extension', 'blockedExtensions']
};

engine.evaluateExpr(safeFileType, upload);
// Result: { success: true }
For checking if value is NOT in a literal array of 2-3 items, notIn is clearer than multiple neq operators combined with and.

Error Handling

Common Errors

const data = {
  user: {
    role: 'admin',
    name: 'John Doe'  // Not an array
  }
};

// Trying to use IN with non-array
const result = engine.evaluateExpr(
  { in: ['admin', 'user.name'] },
  data
);

// Returns:
// {
//   success: false,
//   error: "IN operator requires array as right operand",
//   details: {
//     rightType: "string",
//     originalRight: "user.name"
//   }
// }
const data = {
  user: { role: 'admin' }
  // Missing 'permissions' field
};

// Field doesn't exist
const result = engine.evaluateExpr(
  { in: ['read', 'user.permissions'] },
  data
);

// Returns:
// {
//   success: false,
//   error: "IN operator requires array as right operand"
// }
// Missing second argument
const result = engine.evaluateExpr(
  { in: ['admin'] },
  data
);

// Returns:
// {
//   success: false,
//   error: "IN operator requires 2-3 arguments, got 1"
// }
const data = { role: 'admin' };

// Passing string instead of array
const result = engine.evaluateExpr(
  { in: ['role', 'admin,moderator'] },  // Wrong: string not array
  data
);

// Returns:
// {
//   success: false,
//   error: "IN operator requires array as right operand"
// }

// Correct usage:
const correctResult = engine.evaluateExpr(
  { in: ['role', ['admin', 'moderator']] },  // Correct: array
  data
);
// Result: { success: true }

Safe Error Handling

function safeArrayCheck(engine, operator, value, array, data, fallback = false) {
  // Validate array field exists and is array
  const resolvedArray = engine.resolvePath(data, array);

  if (!Array.isArray(resolvedArray)) {
    console.error(`Field '${array}' is not an array`);
    return fallback;
  }

  const result = engine.evaluateExpr(
    { [operator]: [value, array] },
    data
  );

  if (!result.success) {
    console.error(`Array check failed: ${result.error}`);
    return fallback;
  }

  return result.success;
}

// Usage
const hasPermission = safeArrayCheck(
  engine,
  'in',
  'write',
  'user.permissions',
  userData,
  false
);

Type Coercion & Comparison

Array operators use TypeUtils.isEqual() for element comparison:

Loose Mode (Default)

const data = {
  numbers: [1, 2, 3],
  strings: ['1', '2', '3']
};

// Loose equality - type coercion enabled
engine.evaluateExpr({ in: [1, 'strings'] }, data);
// Result: { success: true } - 1 == '1'

engine.evaluateExpr({ in: ['2', 'numbers'] }, data);
// Result: { success: true } - '2' == 2

Strict Mode

const strictEngine = createRuleEngine({ strict: true });

// Strict equality - no type coercion
strictEngine.evaluateExpr({ in: [1, 'strings'] }, data);
// Result: { success: false } - 1 !== '1'

strictEngine.evaluateExpr({ in: ['2', 'numbers'] }, data);
// Result: { success: false } - '2' !== 2

// Per-rule strict mode
engine.evaluateExpr(
  { in: [1, 'strings', { strict: true }] },
  data
);
// Result: { success: false }

Comparison Table

Value TypeArray ElementLoose ModeStrict Mode
11✅ Match✅ Match
1'1'✅ Match❌ No match
'true'true❌ No match❌ No match
nullundefined❌ No match❌ No match
Use strict mode in production to prevent unexpected type coercion bugs when checking array membership.

Comparison Operators

eq, neq for single value checks

String Operators

contains, startsWith, endsWith

Special Operators

isNull, isNotNull, between

Logical Operators

and, or, not for combining rules

State Operators

changed, changedTo, changedFrom

All Operators

Complete operator reference

API Reference

For complete API documentation: