mobily/ts-belt

Implementation Question

Closed this issue · 3 comments

Could not find discussion panel nor any discord/slack/anything channel for any Q&A.
Let me know if there is a better place to ask questions regarding ts-belt.
I'm kinda new to FP in general and I can't shift my usual non FP thinking.

Is there anyway that I can tackle this with FP/ts-belt??

Scenario: Need to find position (index+1) where the sum of values equals to -1
Example data :

  • [1,1,-1,1,-1,-1,-1,1,1,1] answer is 7
  • [-1] answer is 1
  • [1,1,-1,-1,-1] answer is 5
let position: number | undefined = undefined;

_.pipe(
  [1,1,-1,1,-1,-1,-1,1,1,1],
  _.A.reduceWithIndex(0, (acc, next, index) => {
    const add = _.N.add(acc)(next);

    if (add === -1 && position === undefined) {
      position = index + 1;
    }

    return add;
  })
);

Is there a way to break out from reduce loop? I guess there is not.
How I can possibly hold a value not using an global variable but maintaining functional paradigms?
What would be a better approach for this case with ts-belt?

@stychu your solution looks absolutely fine, beyond using mutable values :) in this specific case, I would use the following approach:

import { pipe, O, A } from '@mobily/ts-belt';

// your array
const xs = [1, 1, -1, -1, -1];

// define a tuple, the first value is the `Option` data type, which holds the `position` value
const initialValue: readonly [O.Option<number>, number] = [O.None, 0];

const position = pipe(
  xs,
  A.reduceWithIndex(initialValue, (acc, next, index) => {
    const [position, value] = acc;
    const sum = value + next;

    return sum === -1
      ? <const>[O.Some(index + 1), sum]
      : <const>[position, sum];
  }),
  // get the first value of the tuple (`position`)
  A.getUnsafe(0),
  // map `Option` to `undefined` if `position` is `O.None`, otherwise it gets the `position` value (you may want to use `O.getWithDefault`)
  O.toUndefined
);

console.log(position) // → 5

see: https://codesandbox.io/s/laughing-knuth-odhr7i?file=/src/utils.ts

alternatively, with no Option data type:

import { pipe, O, A } from '@mobily/ts-belt';

// your array
const xs = [1, 1, -1, -1, -1];

// define a tuple, the first value is the `Option` data type, which holds the `position` value
const initialValue: readonly [number | undefined, number] = [undefined, 0];

const position = pipe(
  xs,
  A.reduceWithIndex(initialValue, (acc, next, index) => {
    const [position, value] = acc;
    const sum = value + next;

    return sum === -1
      ? <const>[index + 1, sum]
      : <const>[position, sum];
  }),
  // get the first value of the tuple (`position`)
  A.getUnsafe(0),
);

console.log(position) // → 5

@mobily Thanks for some examples. I appreciate it ! Yeah the mutable values were my concern mostly :P
Although your example solution seems fine there is one problem with them as they dont give me desired answer.
Problem relies with the reduce continuing the loop always till the end where I need to 'exit' loop whenever first occurrence of sum === -1. With your example if there are multiple occurrences of sum === -1 it will always return the last one where I need the first one and dont care about the rest.

Here is example data set:

const xs = [
  1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1,
  1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
];

It should return 11 instead with your solution I get 35
My bad I didn't mention about the first occurence 😨

This is the only way I can think of solving this at the moment. It works as I need at least :)

const calculateFloorAndPosition = (data: number[]) => {
  const initialValue: [number, number[]] = [0, []];

  return _.pipe(
    data,
    _.A.reduceWithIndex(initialValue, (acc, next, index) => {
      const [value, position] = acc;
      const sum = value + next;

      if (sum === -1) {
        position.push(index + 1);
      }
      return [sum, position] as typeof initialValue;
    })
  );
};

Thanks for input @mobily