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
Array.prototype.slice()
The slice() method returns a shallow copy of a portion of an array into a new array object selected from begin to end (end not included). The original array will not be modified.
Try with for loop with a increment of 2 in each iteration. Pass the current value of i as the start position and i+2 as the end position as the method parameter:
const books =[
{id: "1", name: "twilight", category: "Movies", price: 10},
{id: "2", name: "jaws", category: "Movies", price: 22},
{id: "3", name: "the shining", category: "Movies", price: 1},
{id: "4", name: "beers", category: "Movies", price: 10},
{id: "5", name: "apples", category: "Movies", price: 22},
{id: "6", name: "mono", category: "Movies", price: 1}
]
for(var i=0; i<books.length; i+=2){
var sliced = books.slice(i, i+2);
console.log(sliced);
}
You can use the slice function passing the number of books to slice (i.e. 2):
let slicedBooks = []
for(var i = 0;i < books.length;i+= 2){
let part_slice = books.slice(i, 2 + i);
slicedBooks.push(part_slice);
console.log(part_slice);
}
console.log(slicedBooks);
Be careful slice does not update books array, but returns a new array.
"I want to create a new array containing only those objects where the property country is "United States""
This is exactly what the Array.filter() method is for:
var filteredArray = arr.filter(function(val, i, a) {
return val.country==="United States";
});
Note that the .filter() method isn't available in IE before version 9, but the MDN page I linked to above shows you exactly how to implement it so reading that page should in itself answer your question.
Note also that in the (non-working) code in the question, your two for loops are basically doing the same thing as each other because they're both iterating over arr, so it doesn't make sense to nest them like that. You shouldn't use a for..in loop on an array, but if you do the key values will be the numeric indexes, it doesn't somehow pick up the properties of the object stored at each index.
EDIT:
"Some of the object literals have the same value for a given property, and I want to create a new array containing only those object literals."
OK, re-reading this I guess you didn't really want to select elements by specifying a country, you wanted to select elements for any country that had duplicate entries? So if there were another three elements that all had "New Zealand" you'd want to select them in addition to the "United States" ones? If so, you could do something like this:
var countryCount = {},
i;
for (i = 0; i < arr.length; i++)
if (countryCount.hasOwnProperty(arr[i].country)
countryCount[arr[i].country]++;
else
countryCount[arr[i].country] = 1;
var filteredArr = arr.filter(function(val, i, a) {
return countryCount[val.country] > 1;
});
var getCountry = function (country) {
var out = [];
for (var i = 0, len = arr.length; i < len; i++)
if (arr[i].country === country) out.push(arr[i]);
return out;
};
Demo: http://jsfiddle.net/YwytD/1/
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!
Nobody mentioned Object.entries() yet, which might be the most flexible way to do it. This method uses the same ordering as for..in when enumerating properties, i.e. the order that properties were originally entered in the object. You also get subarrays with both property and value so you can use whichever or both. Finally you don't have to worry about the properties being numerical or setting an extra length property (as you do when using Array.prototype.slice.call()).
Here's an example:
const obj = {'prop1': 'foo', 'prop2': 'bar', 'prop3': 'baz', 'prop4': {'prop': 'buzz'}};
You want to slice the first two values:
Object.entries(obj).slice(0,2).map(entry => entry[1]);
//["foo", "bar"]
All of the keys?
Object.entries(obj).slice(0).map(entry => entry[0]);
//["prop1", "prop2", "prop3", "prop4"]
The last key-value pair?
Object.entries(obj).slice(-1)
//[ ['prop4', {'prop': 'buzz'}] ]
The best modern solution to this is the combination of Object.fromEntries and Object.entries.
const foo = {
one: 'ONE',
two: 'TWO',
three: 'THREE',
four: 'FOUR',
}
const sliced = Object.fromEntries(
Object.entries(foo).slice(1, 3)
)
console.log(sliced)
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.