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

  • Basic Membership
  • Dynamic Arrays
  • Number Arrays
  • Case Sensitivity
  • With Rule Helpers
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

  • Basic Exclusion
  • Account Validation
  • Security Checks
  • Content Filtering
  • Dynamic Exclusion
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.

API Reference

For complete API documentation: