From what I understood from your answers what you need is to filter the array:
Copyconst filteredArray = array.filter(element => {
if (yourConditionIsTrue) { // if this element should be in the filteredArray
return true;
} else {
return false
}
});
Which can be done in one line:
Copyconst filteredArray = array.filter(element => conditionIsTrue);
This way your array remains untouched and you get a new array (filteredArray) only with the elements you need, but you don't mess up with the array you are iterating.
Answer from A. Llorente on Stack OverflowVideos
Yes, you can use conditional type inference on the function type, in a way very similar to how the Parameters utility type is implemented:
type ParametersExceptFirst<F> =
F extends (arg0: any, ...rest: infer R) => any ? R : never;
compare to
// from lib.es5.d.ts
type Parameters<T extends (...args: any) => any> =
T extends (...args: infer P) => any ? P : never;
and verify that it works:
declare function foo(x: string, y: number, z: boolean): Date;
type FooParamsExceptFirst = ParametersExceptFirst<typeof foo>;
// type FooParamsExceptFirst = [y: number, z: boolean]
declare function foo(x: string, y: number, z: boolean): Date;
Playground link to code
UPDATE: arbitrary slicing of tuples with numeric literals is possible, but not pretty and has caveats. First let's write TupleSplit<T, N> which takes a tuple T and a numeric literal type N, and splits the tuple T at index N, returning two pieces: the first piece is the first N elements of T, and the second piece is everything after that. (If N is more than the length of T then the first piece is all of T and the second piece is empty):
type TupleSplit<T, N extends number, O extends readonly any[] = readonly []> =
O['length'] extends N ? [O, T] : T extends readonly [infer F, ...infer R] ?
TupleSplit<readonly [...R], N, readonly [...O, F]> : [O, T]
This works via recursive conditional types on variadic tuples and is therefore more computationally intensive than the relatively simple ParametersExceptFirst implementation above. If you try this on long tuples (lengths more than 25 or so) you can expect to see recursion errors. If you try this on ill-behaved types like non-fixed-length tuples or unions of things, you might get weird results. It's fragile; be careful with it.
Let's verify that it works:
type Test = TupleSplit<readonly ["a", "b", "c", "d", "e"], 3>
// type Test = [readonly ["a", "b", "c"], readonly ["d", "e"]]
Looks good.
Now we can use TupleSplit<T, N> to implement TakeFirst<T, N>, returning just the first N elements of T, and SkipFirst<T, N>, which skips the first N elements of T:
type TakeFirst<T extends readonly any[], N extends number> =
TupleSplit<T, N>[0];
type SkipFirst<T extends readonly any[], N extends number> =
TupleSplit<T, N>[1];
And finally TupleSlice<T, S, E> produces the slice of tuple T from start position S to end position E (remember, slices are inclusive of the start index, and exclusive of the end index) by taking the first E elements of T and skipping the first S elements of the result:
type TupleSlice<T extends readonly any[], S extends number, E extends number> =
SkipFirst<TakeFirst<T, E>, S>
To demonstrate that this more or less represents what array slice() does, let's write a function and test it:
function slice<T extends readonly any[], S extends number, E extends number>(
arr: readonly [...T], start: S, end: E
) {
return arr.slice(start, end) as readonly any[] as TupleSlice<T, S, E>;
}
const tuple = ["a", "b", "c", "d", "e"] as const
// const tuple: readonly ["a", "b", "c", "d", "e"]
const ret0 = slice(tuple, 2, 4);
// const ret0: readonly ["c", "d"]
console.log(ret0); // ["c", "d"]
const ret1 = slice(tuple, 0, 9);
// const ret1: readonly ["a", "b", "c", "d", "e"]
console.log(ret1); // ["a", "b", "c", "d", "e"];
const ret2 = slice(tuple, 5, 3);
// const ret2: readonly []
console.log(ret2); // [];
This looks good; the returned arrays from slice() have types that accurately represent their values.
Of course, there are many caveats; if you pass negative or non-whole numbers to slice() for S and E, then TupleSlice<T, S, E> is very likely not to correspond to what actually happens with array slices: negative "from end" behavior is possibly implementable but it would be even uglier; non-whole numbers or even just number have not been tested but I expect recursion warnings and other things that go bump in the night. Be warned!
Playground link to code
@jcalz accepted solution here has a limitation: it drops tuple labels:
type OriginalTupleSplit = TupleSplit<[a: 1, b: 2, c: 3, d: 4, e: 5], 2>
// [[1, 2], [c: 3, d: 4, e: 5]]
Below is a modified version that doesn't do that:
type TupleSplitHead<T extends any[], N extends number> = T['length'] extends N
? T
: T extends [...infer R, any]
? TupleSplitHead<R, N>
: never
type TupleSplitTail<T, N extends number, O extends any[] = []> = O['length'] extends N
? T
: T extends [infer F, ...infer R]
? TupleSplitTail<[...R], N, [...O, F]>
: never
type TupleSplit<T extends any[], N extends number> = [TupleSplitHead<T, N>, TupleSplitTail<T, N>]
type ModifiedTupleSplit = TupleSplit<[a: 1, b: 2, c: 3, d: 4, e: 5], 2>
// [[a: 1, b: 2], [c: 3, d: 4, e: 5]]
code
This is less performant than @jcalz's solution - as it iterates the array twice, but it keeps the labels.
.splice() returns the deleted elements, but if you use it in a separate line:
Copyvar arr = ['Apple', 'Orange', 'Plums', 'Grapes'];
arr.splice(arr.indexOf('Orange'), 1);
console.log(arr);
Run code snippetEdit code snippet Hide Results Copy to answer Expand
Or you can use .slice() like this: (it's a bit long)
Copyvar arr = ['Apple', 'Orange', 'Plums', 'Grapes'];
//This slices from the start up to "Orange", then concatenates from after "Orange" to the end of the array
console.log(arr.slice(0, arr.indexOf('Orange')).concat(arr.slice( arr.indexOf('Orange') + 1, arr.length)));
Run code snippetEdit code snippet Hide Results Copy to answer Expand
So slice does not work the same as splice
Array.slice takes two parameter, start and end. So in your first function you are giving the start index of Orange and the end index of 1 which I think doesn't make sense because the slice is getting items within a range, so there are no items between that range.
So if you look at the code snippet I have the index of Orange and then the index one up because the slice is inclusive so you in your example Orange is at the 1 index and then you are doing 1 as the end index. So I am pulling everything between index 1 and index 2, which is Orange.
Copylet newArray;
let mutatedArray;
function removeOneFruit() {
newArray=['Apple', 'Orange', 'Plums', 'Grapes'];
console.log("Before slicing: "+newArray);
mutatedArray=newArray.slice(newArray.indexOf('Orange'), newArray.indexOf('Orange')+1);
console.log("After slicing: "+mutatedArray);
}
removeOneFruit()
Run code snippetEdit code snippet Hide Results Copy to answer Expand
Your second function you are using splice which is used to remove items from an array. Array.splice takes an index and the amount of items you want to remove at that index. So all you are doing is creating a new array with the item that you remove. If you wanted return an array with everything but Orange. Then you would run splice, which would remove Orange, and then point the mutatedArray to the new value of newArray.
Copylet newArray;
let mutatedArray;
function removeOneFruit() {
newArray=['Apple', 'Orange', 'Plums', 'Grapes'];
console.log("Before splicing: "+newArray);
newArray.splice(newArray.indexOf('Orange'),1);
mutatedArray= newArray;
console.log("After splicing: "+mutatedArray);
}
removeOneFruit()
Run code snippetEdit code snippet Hide Results Copy to answer Expand
EDIT: Thanks for the answers, magnificent people! So Array.from is a better way than [].slice.call, and they're used to provide the array prototype to array-like objects! If done directly, apparently errors are thrown.
Thanks!