Repository: remeda/remeda
Pull request #722 (2024-06-07 21:14:21)

fix(pipe): early exit on hasMany
Currently when using `pipe`, `take` does nothing if it is followed by `flat` or `flatMap`. Here is a small example:

```ts
import * as R from 'remeda';

console.log(
  R.pipe(
    [
      [1, 2],
      [3, 4],
      [5, 6],
    ],
    R.take(1),
    R.flat()
  )
);
```

This code should output to the console `[1, 2]`, but actually outputs `[1, 2, 3, 4, 5, 6]`
This is because using `take` causes `isDone` to change to `true`, but `flat` (or any other function with `hasMany` in its iterator) has an early return of `false`, that ignores the value of `isDone` (so, instead of taking only first pair in the example code, `pipe` will take all pairs)

---

Make sure that you:

- [ ] Typedoc added for new methods and updated for changed
- [x] Tests added for new methods and updated for changed
- [ ] New methods added to `src/index.ts`
- [ ] New methods added to `docs/src/pages/mapping.md`

---

<details><summary>We use semantic PR titles to automate the release process!</summary>

https://conventionalcommits.org

PRs should be titled following using the format: `< TYPE >(< scope >)?: description`

### Available Types:

- `feat`: new functions, and changes to a function's type that would impact users.
- `fix`: changes to the runtime behavior of an existing function, or refinements to it's type that shouldn't impact most users.
- `perf`: changes to function implementations that improve a functions _runtime_ performance.
- `refactor`: changes to function implementations that are neither `fix` nor `perf`
- `test`: tests-only changes (transparent to users of the function).
- `docs`: changes to the documentation of a function **or the documentation site**.
- `build`, `ci`, `style`, `chore`, and `revert`: are only relevant for the internals of the library.

For scope put the name of the function you are working on (either new or
existing).

</details>


codesandbox-ci[bot]: This pull request is automatically built and testable in [CodeSandbox](https://codesandbox.io).

  To see build info of the built libraries, click [here](https://ci.codesandbox.io/status/remeda/remeda/pr/722/builds/512967) or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 570a31c342859eca0940f29c2987e36ea7529c70:

|Sandbox| Source |
|--|--|
|[remeda-example-vanilla](https://codesandbox.io/s/remeda-example-vanilla-fxmrjm)| Configuration |

codesandbox-ci[bot]: This pull request is automatically built and testable in [CodeSandbox](https://codesandbox.io).

  To see build info of the built libraries, click [here](https://ci.codesandbox.io/status/remeda/remeda/pr/722/builds/513083) or the icon next to each commit SHA.

Latest deployment of this branch, based on commit e752108997e384e7b54c481d2bf579f9d7d55715:

|Sandbox| Source |
|--|--|
|[remeda-example-vanilla](https://codesandbox.io/s/remeda-example-vanilla-5zq4zj)| Configuration |


Patch:
diff --git a/src/pipe.ts b/src/pipe.ts
index feda29e..b0234ef 100644
--- a/src/pipe.ts
+++ b/src/pipe.ts
@@ -332,7 +332,7 @@ function processItem(
             return true;
           }
         }
-        return false;
+        return isDone;
       }
       currentItem = lazyResult.next;
     }
@@ -348,10 +348,7 @@ function processItem(
   if (lazyResult.hasNext) {
     accumulator.push(currentItem);
   }
-  if (isDone) {
-    return true;
-  }
-  return false;
+  return isDone;
 }
 
 function prepareLazyOperation(op: LazyOp): PreparedLazyOperation {


Test patch:
diff --git a/src/pipe.test.ts b/src/pipe.test.ts
index 97387b9..f4aacf8 100644
--- a/src/pipe.test.ts
+++ b/src/pipe.test.ts
@@ -1,4 +1,5 @@
 import { filter } from "./filter";
+import { flat } from "./flat";
 import { identity } from "./identity";
 import { map } from "./map";
 import { pipe } from "./pipe";
@@ -115,4 +116,17 @@ describe("lazy", () => {
     expect(count2).toHaveBeenCalledTimes(2);
     expect(result).toEqual([100, 200]);
   });
+
+  it("lazy early exit with hasMany", () => {
+    const result = pipe(
+      [
+        [1, 2],
+        [3, 4],
+        [5, 6],
+      ],
+      take(1),
+      flat(),
+    );
+    expect(result).toEqual([1, 2]);
+  });
 });