How to add a value in an array to the values before and after it

后端 未结 4 1182
说谎
说谎 2021-01-26 04:06

I am trying to turn an array of numbers into steps of the value of the Non-Zero integer element i.e

spread([0,0,n,0,0] returns => 
[0 + n-2, 0 + n-1, n, 0 +         


        
4条回答
  •  借酒劲吻你
    2021-01-26 05:05

    One fairly simple technique, if not the most efficient, is to create a square grid with a row for each value, either all zeros or descending from a single non-zero value as described and then simply add the columns.

    Here is my version:

    const pop = arr => arr.map(
      (n, i) => n == 0 
        ? Array(arr.length).fill(0) 
        : arr.map((_, j) => Math.max(n - Math.abs(j - i), 0))
    ).reduce((as, bs) => as.map((a, i) => a + bs[i]))
    
    console.log(...pop([0, 0, 2, 0, 0]))             //~> [0, 1, 2, 1, 0]
    console.log(...pop([3, 0, 0, 0]))                //~> [3, 2, 1, 0]
    console.log(...pop([0, 0, 0, 3, 0, 2, 0]))       //~> [0, 1, 2, 3, 3, 3, 1]
    console.log(...pop([0, 0, 0, 4, 0, 0, 0]))       //~> [1, 2, 3, 4, 3, 2, 1]
    console.log(...pop([0, 0, 0, 0, 4, 0, 0, 3, 0])) //~> [0, 1, 2, 3, 4, 4, 4, 4, 2]

    Note that the intermediate result (after the map, before the reduce) for that last one looks like this:

    [
      [0, 0, 0, 0, 0, 0, 0, 0, 0],
      [0, 0, 0, 0, 0, 0, 0, 0, 0],
      [0, 0, 0, 0, 0, 0, 0, 0, 0],
      [0, 0, 0, 0, 0, 0, 0, 0, 0],
      [0, 1, 2, 3, 4, 3, 2, 1, 0],
      [0, 0, 0, 0, 0, 0, 0, 0, 0],
      [0, 0, 0, 0, 0, 0, 0, 0, 0],
      [0, 0, 0, 0, 0, 1, 2, 3, 2],
      [0, 0, 0, 0, 0, 0, 0, 0, 0],
    ]
    

    From there it's just a matter of adding the columns, a simple reduce call.

    Update

    NinaScholz's comment made me rethink a bit, and I realized that it's just as easy to do an initial reduce rather than a map and only create the arrays that are necessary. This change should be more efficient:

    const spread = arr => arr.reduce(
      (a, n, i) => n == 0 
        ? a 
        : a.concat([arr.map((_, j) => Math.max(n - Math.abs(j - i), 0))]),
      []
    ).reduce((as, bs) => as.map((a, i) => a + bs[i]))
    
    console.log(...spread([0, 0, 2, 0, 0]))             //~> [0, 1, 2, 1, 0]
    console.log(...spread([3, 0, 0, 0]))                //~> [3, 2, 1, 0]
    console.log(...spread([0, 0, 0, 3, 0, 2, 0]))       //~> [0, 1, 2, 3, 3, 3, 1]
    console.log(...spread([0, 0, 0, 4, 0, 0, 0]))       //~> [1, 2, 3, 4, 3, 2, 1]
    console.log(...spread([0, 0, 0, 0, 4, 0, 0, 3, 0])) //~> [0, 1, 2, 3, 4, 4, 4, 4, 2]

    With this change, the intermediate result (between the two reduce calls now) would only consist of

    [
      [0, 1, 2, 3, 4, 3, 2, 1, 0],
      [0, 0, 0, 0, 0, 1, 2, 3, 2],
    ]
    

    Update 2

    Seeing this again, I realize that the last change didn't go far enough. We can eliminate all the intermediate arrays except for one used as a reduce accumulator simply by adding to the current value as we go.

    Here is what I hope is my final version:

    const spread = arr => arr.reduce(
      (a, n, i) => n == 0 
        ? a 
        : arr.map((_, j) => a[j] + Math.max(n - Math.abs(j - i), 0)),
      Array(arr.length).fill(0)
    )
    
    
    console.log(...spread([0, 0, 2, 0, 0]))             //~> [0, 1, 2, 1, 0]
    console.log(...spread([3, 0, 0, 0]))                //~> [3, 2, 1, 0]
    console.log(...spread([0, 0, 0, 3, 0, 2, 0]))       //~> [0, 1, 2, 3, 3, 3, 1]
    console.log(...spread([0, 0, 0, 4, 0, 0, 0]))       //~> [1, 2, 3, 4, 3, 2, 1]
    console.log(...spread([0, 0, 0, 0, 4, 0, 0, 3, 0])) //~> [0, 1, 2, 3, 4, 4, 4, 4, 2

    This has no intermediate data structures except for that accumulator. It does only the arithmetic necessary. AFAICT, this is as efficient as it can get, modulo working with reduce rather than a primitive for-loop. I'd love to know if I'm missing something, though.

提交回复
热议问题