Iterate an array as a pair (current, next) in JavaScript

前端 未结 13 922
情话喂你
情话喂你 2020-12-09 09:06

In the question Iterate a list as pair (current, next) in Python, the OP is interested in iterating a Python list as a series of current, next pairs. I have th

相关标签:
13条回答
  • 2020-12-09 09:42

    Lodash does have a method that allows you to do this: https://lodash.com/docs#chunk

    _.chunk(array, 2).forEach(function(pair) {
      var first = pair[0];
      var next = pair[1];
      console.log(first, next)
    })
    
    0 讨论(0)
  • 2020-12-09 09:48

    This answer is inspired by an answer I saw to a similar question but in Haskell: https://stackoverflow.com/a/4506000/5932012

    We can use helpers from Lodash to write the following:

    const zipAdjacent = function<T> (ts: T[]): [T, T][] {
      return zip(dropRight(ts, 1), tail(ts));
    };
    zipAdjacent([1,2,3,4]); // => [[1,2], [2,3], [3,4]]
    

    (Unlike the Haskell equivalent, we need dropRight because Lodash's zip behaves differently to Haskell's`: it will use the length of the longest array instead of the shortest.)

    The same in Ramda:

    const zipAdjacent = function<T> (ts: T[]): [T, T][] {
      return R.zip(ts, R.tail(ts));
    };
    zipAdjacent([1,2,3,4]); // => [[1,2], [2,3], [3,4]]
    

    Although Ramda already has a function that covers this called aperture. This is slightly more generic because it allows you to define how many consecutive elements you want, instead of defaulting to 2:

    R.aperture(2, [1,2,3,4]); // => [[1,2], [2,3], [3,4]]
    R.aperture(3, [1,2,3,4]); // => [[1,2,3],[2,3,4]]
    
    0 讨论(0)
  • 2020-12-09 09:49

    d3.js provides a built-in version of what is called in certain languages a sliding:

    console.log(d3.pairs([1, 2, 3, 4])); // [[1, 2], [2, 3], [3, 4]]
    <script src="http://d3js.org/d3.v5.min.js"></script>

    # d3.pairs(array[, reducer]) <>

    For each adjacent pair of elements in the specified array, in order, invokes the specified reducer function passing the element i and element i - 1. If a reducer is not specified, it defaults to a function which creates a two-element array for each pair.

    0 讨论(0)
  • 2020-12-09 09:50

    We can wrap Array.reduce a little to do this, and keep everything clean. Loop indices / loops / external libraries are not required.

    If the result is required, just create an array to collect it.

    function pairwiseEach(arr, callback) {
      arr.reduce((prev, current) => {
        callback(prev, current)
        return current
      })
    }
    
    function pairwise(arr, callback) {
      const result = []
      arr.reduce((prev, current) => {
        result.push(callback(prev, current))
        return current
      })
      return result
    }
    
    const arr = [1, 2, 3, 4]
    pairwiseEach(arr, (a, b) => console.log(a, b))
    const result = pairwise(arr, (a, b) => [a, b])
    
    const output = document.createElement('pre')
    output.textContent = JSON.stringify(result)
    document.body.appendChild(output)

    0 讨论(0)
  • 2020-12-09 09:50

    Simply use forEach with all its parameters for this:

    yourArray.forEach((current, idx, self) => {
      if (let next = self[idx + 1]) {
        //your code here
      }
    })
    
    0 讨论(0)
  • 2020-12-09 09:54

    Here's a generic functional solution without any dependencies:

    const nWise = (n, array) => {
      iterators = Array(n).fill()
        .map(() => array[Symbol.iterator]());
      iterators
        .forEach((it, index) => Array(index).fill()
          .forEach(() => it.next()));
      return Array(array.length - n + 1).fill()
        .map(() => (iterators
          .map(it => it.next().value);
    };
    
    const pairWise = (array) => nWise(2, array);
    

    I know doesn't look nice at all but by introducing some generic utility functions we can make it look a lot nicer:

    const sizedArray = (n) => Array(n).fill();
    

    I could use sizedArray combined with forEach for times implementation, but that'd be an inefficient implementation. IMHO it's ok to use imperative code for such a self-explanatory function:

    const times = (n, cb) => {
      while (0 < n--) {
        cb();
      }
    }
    

    If you're interested in more hardcore solutions, please check this answer.

    Unfortunately Array.fill only accepts a single value, not a callback. So Array(n).fill(array[Symbol.iterator]()) would put the same value in every position. We can get around this the following way:

    const fillWithCb = (n, cb) => sizedArray(n).map(cb);
    

    The final implementation:

    const nWise = (n, array) => {
      iterators = fillWithCb(n, () => array[Symbol.iterator]());
      iterators.forEach((it, index) => times(index, () => it.next()));
      return fillWithCb(
        array.length - n + 1,
        () => (iterators.map(it => it.next().value),
      );
    };
    

    By changing the parameter style to currying, the definition of pairwise would look a lot nicer:

    const nWise = n => array => {
      iterators = fillWithCb(n, () => array[Symbol.iterator]());
      iterators.forEach((it, index) => times(index, () => it.next()));
      return fillWithCb(
        array.length - n + 1,
        () => iterators.map(it => it.next().value),
      );
    };
    
    const pairWise = nWise(2);
    

    And if you run this you get:

    > pairWise([1, 2, 3, 4, 5]);
    // [ [ 1, 2 ], [ 2, 3 ], [ 3, 4 ], [ 4, 5 ] ]
    
    0 讨论(0)
提交回复
热议问题