JavaScript Basics

JavaScript Array values() Method: Iterate Over Array Elements

Learn the JavaScript Array values() method to create iterators for array values. Master iteration patterns, iterator protocol, and practical use cases.

By JavaScript Document Team
arraysmethodsbasicsiteratorses6

The values() method returns a new Array Iterator object that contains the values for each index in the array. It's part of the ES6 iterator protocol and provides a consistent way to iterate over array elements.

Understanding Array values()

The values() method creates an iterator that yields the values of an array in order. While arrays are already iterable by default (using for...of loops), the values() method provides explicit access to the value iterator.

Syntax

array.values();

Parameters

The values() method takes no parameters.

Return Value

A new Array Iterator object that yields array values.

Basic Usage

Simple Iteration

const fruits = ['apple', 'banana', 'cherry'];

// Get the iterator
const iterator = fruits.values();

// Iterate manually using next()
console.log(iterator.next()); // { value: 'apple', done: false }
console.log(iterator.next()); // { value: 'banana', done: false }
console.log(iterator.next()); // { value: 'cherry', done: false }
console.log(iterator.next()); // { value: undefined, done: true }

Using for...of Loop

const colors = ['red', 'green', 'blue', 'yellow'];

// Iterate over values using for...of
for (const value of colors.values()) {
  console.log(value);
}
// Output:
// red
// green
// blue
// yellow

// Note: This is equivalent to:
for (const value of colors) {
  console.log(value);
}

Working with Iterators

Converting to Array

const numbers = [10, 20, 30, 40, 50];

// Convert iterator to array using spread operator
const valueArray = [...numbers.values()];
console.log(valueArray); // [10, 20, 30, 40, 50]

// Using Array.from()
const valuesFromIterator = Array.from(numbers.values());
console.log(valuesFromIterator); // [10, 20, 30, 40, 50]

// Creating a copy of array
const original = [1, 2, 3];
const copy = [...original.values()];
console.log(copy); // [1, 2, 3]
console.log(copy === original); // false (different arrays)

Iterator Protocol Implementation

const arr = ['a', 'b', 'c'];
const iter = arr.values();

// values() returns an iterator that implements the iterator protocol
console.log(typeof iter.next); // 'function'
console.log(Symbol.iterator in iter); // true

// The iterator is also iterable
for (const value of iter) {
  console.log(value); // a, b, c
}

// Once consumed, the iterator is exhausted
console.log(iter.next()); // { value: undefined, done: true }

Practical Examples

Processing Array Elements

class ArrayProcessor {
  static *processValues(arr, transformFn) {
    for (const value of arr.values()) {
      yield transformFn(value);
    }
  }

  static async *processValuesAsync(arr, asyncTransformFn) {
    for (const value of arr.values()) {
      yield await asyncTransformFn(value);
    }
  }

  static takeValues(arr, n) {
    const result = [];
    const iterator = arr.values();

    for (let i = 0; i < n; i++) {
      const { value, done } = iterator.next();
      if (done) break;
      result.push(value);
    }

    return result;
  }
}

// Synchronous processing
const numbers = [1, 2, 3, 4, 5];
const doubled = [...ArrayProcessor.processValues(numbers, (x) => x * 2)];
console.log(doubled); // [2, 4, 6, 8, 10]

// Taking first n values
console.log(ArrayProcessor.takeValues(numbers, 3)); // [1, 2, 3]
console.log(ArrayProcessor.takeValues(numbers, 10)); // [1, 2, 3, 4, 5]

Filtering with Iterators

function* filterIterator(iterator, predicate) {
  for (const value of iterator) {
    if (predicate(value)) {
      yield value;
    }
  }
}

const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const evenValues = filterIterator(data.values(), (x) => x % 2 === 0);

console.log([...evenValues]); // [2, 4, 6, 8, 10]

// Chaining iterator operations
function* mapIterator(iterator, mapFn) {
  for (const value of iterator) {
    yield mapFn(value);
  }
}

const pipeline = mapIterator(
  filterIterator(data.values(), (x) => x % 2 === 0),
  (x) => x * x
);

console.log([...pipeline]); // [4, 16, 36, 64, 100]

Parallel Iteration

class ParallelIterator {
  static *zip(...arrays) {
    const iterators = arrays.map((arr) => arr.values());

    while (true) {
      const results = iterators.map((iter) => iter.next());

      if (results.some((result) => result.done)) {
        break;
      }

      yield results.map((result) => result.value);
    }
  }

  static *zipLongest(...arrays) {
    const iterators = arrays.map((arr) => arr.values());

    while (true) {
      const results = iterators.map((iter) => iter.next());

      if (results.every((result) => result.done)) {
        break;
      }

      yield results.map((result) => (result.done ? undefined : result.value));
    }
  }
}

const arr1 = ['a', 'b', 'c'];
const arr2 = [1, 2, 3, 4];
const arr3 = [true, false];

console.log([...ParallelIterator.zip(arr1, arr2, arr3)]);
// [['a', 1, true], ['b', 2, false]]

console.log([...ParallelIterator.zipLongest(arr1, arr2, arr3)]);
// [['a', 1, true], ['b', 2, false], ['c', 3, undefined], [undefined, 4, undefined]]

Advanced Patterns

Lazy Evaluation

class LazyArray {
  constructor(generator) {
    this.generator = generator;
  }

  *values() {
    yield* this.generator();
  }

  take(n) {
    const result = [];
    const iter = this.values();

    for (let i = 0; i < n; i++) {
      const { value, done } = iter.next();
      if (done) break;
      result.push(value);
    }

    return result;
  }

  filter(predicate) {
    const self = this;
    return new LazyArray(function* () {
      for (const value of self.values()) {
        if (predicate(value)) yield value;
      }
    });
  }

  map(fn) {
    const self = this;
    return new LazyArray(function* () {
      for (const value of self.values()) {
        yield fn(value);
      }
    });
  }

  toArray() {
    return [...this.values()];
  }
}

// Infinite sequence
const fibonacci = new LazyArray(function* () {
  let a = 0,
    b = 1;
  while (true) {
    yield a;
    [a, b] = [b, a + b];
  }
});

console.log(fibonacci.take(10));
// [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

const evenFibs = fibonacci.filter((n) => n % 2 === 0).map((n) => n * 2);

console.log(evenFibs.take(5));
// [0, 4, 16, 68, 288]

Custom Iterator Combinations

class IteratorCombinator {
  static *concat(...iterables) {
    for (const iterable of iterables) {
      yield* iterable.values ? iterable.values() : iterable;
    }
  }

  static *unique(iterable) {
    const seen = new Set();
    const iterator = iterable.values ? iterable.values() : iterable;

    for (const value of iterator) {
      if (!seen.has(value)) {
        seen.add(value);
        yield value;
      }
    }
  }

  static *chunk(iterable, size) {
    const iterator = iterable.values ? iterable.values() : iterable;
    let chunk = [];

    for (const value of iterator) {
      chunk.push(value);
      if (chunk.length === size) {
        yield chunk;
        chunk = [];
      }
    }

    if (chunk.length > 0) {
      yield chunk;
    }
  }
}

const arr1 = [1, 2, 3];
const arr2 = [3, 4, 5];
const arr3 = [5, 6, 7];

// Concatenate arrays
const concatenated = [...IteratorCombinator.concat(arr1, arr2, arr3)];
console.log(concatenated); // [1, 2, 3, 3, 4, 5, 5, 6, 7]

// Get unique values
const unique = [...IteratorCombinator.unique(concatenated)];
console.log(unique); // [1, 2, 3, 4, 5, 6, 7]

// Chunk values
const chunked = [...IteratorCombinator.chunk([1, 2, 3, 4, 5, 6, 7], 3)];
console.log(chunked); // [[1, 2, 3], [4, 5, 6], [7]]

State Management with Iterators

class StatefulIterator {
  constructor(array) {
    this.array = array;
    this.position = 0;
  }

  values() {
    const self = this;
    return {
      [Symbol.iterator]() {
        return this;
      },
      next() {
        if (self.position < self.array.length) {
          return {
            value: self.array[self.position++],
            done: false,
          };
        }
        return { done: true };
      },
    };
  }

  reset() {
    this.position = 0;
  }

  skip(n) {
    this.position = Math.min(this.position + n, this.array.length);
    return this;
  }

  peek() {
    return this.position < this.array.length
      ? this.array[this.position]
      : undefined;
  }
}

const stateful = new StatefulIterator([1, 2, 3, 4, 5]);

console.log([...stateful.values()]); // [1, 2, 3, 4, 5]
console.log([...stateful.values()]); // [] (already consumed)

stateful.reset();
stateful.skip(2);
console.log([...stateful.values()]); // [3, 4, 5]

Comparison with Other Methods

values() vs Default Iteration

const arr = ['a', 'b', 'c'];

// Default iteration (implicit values())
for (const value of arr) {
  console.log(value); // a, b, c
}

// Explicit values() method
for (const value of arr.values()) {
  console.log(value); // a, b, c
}

// Both are equivalent for arrays
console.log(arr[Symbol.iterator] === arr.values); // true

values() vs entries() vs keys()

const items = ['apple', 'banana', 'cherry'];

// values() - returns values only
console.log([...items.values()]); // ['apple', 'banana', 'cherry']

// keys() - returns indices only
console.log([...items.keys()]); // [0, 1, 2]

// entries() - returns [index, value] pairs
console.log([...items.entries()]);
// [[0, 'apple'], [1, 'banana'], [2, 'cherry']]

// Practical comparison
function processArray(arr, type = 'values') {
  switch (type) {
    case 'values':
      return [...arr.values()];
    case 'keys':
      return [...arr.keys()];
    case 'entries':
      return [...arr.entries()];
    default:
      throw new Error('Invalid type');
  }
}

Working with Different Data Types

Handling Mixed Types

const mixed = [
  42,
  'hello',
  true,
  null,
  undefined,
  { name: 'object' },
  [1, 2, 3],
  new Date(),
  /regex/,
  Symbol('sym'),
];

// Categorize values by type
function categorizeValues(arr) {
  const categories = {
    numbers: [],
    strings: [],
    booleans: [],
    objects: [],
    arrays: [],
    nullish: [],
    other: [],
  };

  for (const value of arr.values()) {
    if (value === null || value === undefined) {
      categories.nullish.push(value);
    } else if (typeof value === 'number') {
      categories.numbers.push(value);
    } else if (typeof value === 'string') {
      categories.strings.push(value);
    } else if (typeof value === 'boolean') {
      categories.booleans.push(value);
    } else if (Array.isArray(value)) {
      categories.arrays.push(value);
    } else if (typeof value === 'object') {
      categories.objects.push(value);
    } else {
      categories.other.push(value);
    }
  }

  return categories;
}

console.log(categorizeValues(mixed));

Sparse Array Behavior

// values() includes undefined for empty slots
const sparse = [1, , , 4, , 6];

console.log([...sparse.values()]); // [1, undefined, undefined, 4, undefined, 6]

// Compare with forEach which skips empty slots
const forEachValues = [];
sparse.forEach((v) => forEachValues.push(v));
console.log(forEachValues); // [1, 4, 6]

// Function to get only defined values
function* definedValues(arr) {
  for (let i = 0; i < arr.length; i++) {
    if (i in arr) {
      yield arr[i];
    }
  }
}

console.log([...definedValues(sparse)]); // [1, 4, 6]

Performance Considerations

// Performance comparison
const largeArray = Array.from({ length: 100000 }, (_, i) => i);

// Using values() iterator
console.time('values iterator');
let sum1 = 0;
for (const value of largeArray.values()) {
  sum1 += value;
}
console.timeEnd('values iterator');

// Using default iteration
console.time('default iteration');
let sum2 = 0;
for (const value of largeArray) {
  sum2 += value;
}
console.timeEnd('default iteration');

// Using traditional for loop
console.time('for loop');
let sum3 = 0;
for (let i = 0; i < largeArray.length; i++) {
  sum3 += largeArray[i];
}
console.timeEnd('for loop');

// Traditional for loops are typically fastest for simple operations

Best Practices

  1. Use Default Iteration: For arrays, default for...of is equivalent to values()
  2. Explicit Intent: Use values() when you want to be explicit about iterating values
  3. Iterator Protocol: Useful when working with generic iterator utilities
  4. Memory Efficiency: Iterators don't create intermediate arrays
  5. Combine with Other Methods: Use with keys() and entries() for complete solutions

Conclusion

The values() method provides an explicit way to create an iterator for array values, following the ES6 iterator protocol. While arrays are already iterable by default, values() is useful when you need to work with iterators explicitly, create custom iteration patterns, or build generic utilities that work with different iterator types. Understanding how to use values() effectively, especially in combination with other iterator methods, enables more flexible and powerful array processing patterns.