Skip to content

Commit 8e96368

Browse files
thewilkybarkidtim-smart
authored andcommitted
Add Array.removeOption and Chunk.removeOption (#4861)
Co-authored-by: Tim <hello@timsmart.co>
1 parent 7298ee5 commit 8e96368

File tree

5 files changed

+89
-7
lines changed

5 files changed

+89
-7
lines changed

.changeset/six-pumpkins-yell.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"effect": minor
3+
---
4+
5+
Add Array.removeOption and Chunk.removeOption

packages/effect/src/Array.ts

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1257,8 +1257,15 @@ export const modify: {
12571257
): ReadonlyArray.With<S, ReadonlyArray.Infer<S> | B>
12581258
} = dual(
12591259
3,
1260-
<A, B>(self: Iterable<A>, i: number, f: (a: A) => B): Array<A | B> =>
1261-
Option.getOrElse(modifyOption(self, i, f), () => Array.from(self))
1260+
<A, B>(self: Iterable<A>, i: number, f: (a: A) => B): Array<A | B> => {
1261+
const out: Array<A | B> = Array.from(self)
1262+
if (isOutOfBounds(i, out)) {
1263+
return out
1264+
}
1265+
const b = f(out[i] as A)
1266+
out[i] = b
1267+
return out
1268+
}
12621269
)
12631270

12641271
/**
@@ -1291,11 +1298,11 @@ export const modifyOption: {
12911298
f: (a: ReadonlyArray.Infer<S>) => B
12921299
): Option.Option<ReadonlyArray.With<S, ReadonlyArray.Infer<S> | B>>
12931300
} = dual(3, <A, B>(self: Iterable<A>, i: number, f: (a: A) => B): Option.Option<Array<A | B>> => {
1294-
const arr = Array.from(self)
1301+
const arr = fromIterable(self)
12951302
if (isOutOfBounds(i, arr)) {
12961303
return Option.none()
12971304
}
1298-
const out: Array<A | B> = arr
1305+
const out: Array<A | B> = Array.isArray(self) ? self.slice() : arr
12991306
const b = f(arr[i])
13001307
out[i] = b
13011308
return Option.some(out)
@@ -1332,6 +1339,38 @@ export const remove: {
13321339
return out
13331340
})
13341341

1342+
/**
1343+
* Delete the element at the specified index, creating a new `Array`,
1344+
* or return `None` if the index is out of bounds.
1345+
*
1346+
* @example
1347+
* ```ts
1348+
* import * as assert from "node:assert"
1349+
* import { Array, Option } from "effect"
1350+
*
1351+
* const numbers = [1, 2, 3, 4]
1352+
* const result = Array.removeOption(numbers, 2)
1353+
* assert.deepStrictEqual(result, Option.some([1, 2, 4]))
1354+
*
1355+
* const outOfBoundsResult = Array.removeOption(numbers, 5)
1356+
* assert.deepStrictEqual(outOfBoundsResult, Option.none())
1357+
* ```
1358+
*
1359+
* @since 3.16.0
1360+
*/
1361+
export const removeOption: {
1362+
(i: number): <A>(self: Iterable<A>) => Option.Option<Array<A>>
1363+
<A>(self: Iterable<A>, i: number): Option.Option<Array<A>>
1364+
} = dual(2, <A>(self: Iterable<A>, i: number): Option.Option<Array<A>> => {
1365+
const arr = fromIterable(self)
1366+
if (isOutOfBounds(i, arr)) {
1367+
return Option.none()
1368+
}
1369+
const out = Array.isArray(self) ? self.slice() : arr
1370+
out.splice(i, 1)
1371+
return Option.some(out)
1372+
})
1373+
13351374
/**
13361375
* Reverse an `Iterable`, creating a new `Array`.
13371376
*

packages/effect/src/Chunk.ts

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1238,7 +1238,24 @@ export const remove: {
12381238
<A>(self: Chunk<A>, i: number): Chunk<A>
12391239
} = dual(
12401240
2,
1241-
<A>(self: Chunk<A>, i: number): Chunk<A> => unsafeFromArray(RA.remove(toReadonlyArray(self), i))
1241+
<A>(self: Chunk<A>, i: number): Chunk<A> => {
1242+
if (i < 0 || i >= self.length) return self
1243+
return unsafeFromArray(RA.remove(toReadonlyArray(self), i))
1244+
}
1245+
)
1246+
1247+
/**
1248+
* @since 3.16.0
1249+
*/
1250+
export const removeOption: {
1251+
(i: number): <A>(self: Chunk<A>) => Option<Chunk<A>>
1252+
<A>(self: Chunk<A>, i: number): Option<Chunk<A>>
1253+
} = dual(
1254+
2,
1255+
<A>(self: Chunk<A>, i: number): Option<Chunk<A>> => {
1256+
if (i < 0 || i >= self.length) return O.none()
1257+
return O.some(unsafeFromArray(RA.remove(toReadonlyArray(self), i)))
1258+
}
12421259
)
12431260

12441261
/**
@@ -1249,8 +1266,10 @@ export const modifyOption: {
12491266
<A, B>(self: Chunk<A>, i: number, f: (a: A) => B): Option<Chunk<A | B>>
12501267
} = dual(
12511268
3,
1252-
<A, B>(self: Chunk<A>, i: number, f: (a: A) => B): Option<Chunk<A | B>> =>
1253-
O.map(RA.modifyOption(toReadonlyArray(self), i, f), unsafeFromArray)
1269+
<A, B>(self: Chunk<A>, i: number, f: (a: A) => B): Option<Chunk<A | B>> => {
1270+
if (i < 0 || i >= self.length) return O.none()
1271+
return O.some(unsafeFromArray(RA.modify(toReadonlyArray(self), i, f)))
1272+
}
12541273
)
12551274

12561275
/**

packages/effect/test/Array.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,20 @@ describe("Array", () => {
452452
deepStrictEqual(pipe(new Set([1, 2, 3]), Arr.remove(10)), [1, 2, 3])
453453
})
454454

455+
it("removeOption", () => {
456+
assertSome(pipe([1, 2, 3], Arr.removeOption(0)), [2, 3])
457+
// out of bound
458+
assertNone(pipe([], Arr.removeOption(0)))
459+
assertNone(pipe([1, 2, 3], Arr.removeOption(-1)))
460+
assertNone(pipe([1, 2, 3], Arr.removeOption(10)))
461+
462+
assertSome(pipe(new Set([1, 2, 3]), Arr.removeOption(0)), [2, 3])
463+
// out of bound
464+
assertNone(pipe(new Set([]), Arr.removeOption(0)))
465+
assertNone(pipe(new Set([1, 2, 3]), Arr.removeOption(-1)))
466+
assertNone(pipe(new Set([1, 2, 3]), Arr.removeOption(10)))
467+
})
468+
455469
it("reverse", () => {
456470
deepStrictEqual(Arr.reverse([]), [])
457471
deepStrictEqual(Arr.reverse([1]), [1])

packages/effect/test/Chunk.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,11 @@ describe("Chunk", () => {
116116
assertEquals(pipe(Chunk.make(1, 2, 3), Chunk.remove(0)), Chunk.make(2, 3))
117117
})
118118

119+
it("removeOption", () => {
120+
assertNone(pipe(Chunk.empty(), Chunk.removeOption(0)))
121+
assertSome(pipe(Chunk.make(1, 2, 3), Chunk.removeOption(0)), Chunk.make(2, 3))
122+
})
123+
119124
it("chunksOf", () => {
120125
assertEquals(pipe(Chunk.empty(), Chunk.chunksOf(2)), Chunk.empty())
121126
assertEquals(

0 commit comments

Comments
 (0)