I want to populate orders
which is an array of type Order
. The expected result is orders=[{id:1,qt:4},{id:2, qt:2},{id:3,qt:2}]
. How to do so in TypeScript? I am new to it.
export class Product {
constructor(public id: number, public name: string, public price: number) {}
}
export interface Order {
id: number;
qt: number;
}
export const products: Product[] = [
new Product(1, 'Apple', 2.1),
new Product(2, 'Banana', 2.2),
new Product(3, 'Chocolate', 2.3),
new Product(4, 'Dessert', 2.4),
];
export const cart: Product[] = [
products[0],
products[0],
products[2],
products[1],
products[2],
products[0],
products[1],
products[0],
];
export const orders: Order[] = [];
Edit
For those who want to know how
orders=[{id:1,qt:4},{id:2, qt:2},{id:3,qt:2}]
is obtained.
In the cart
:
- the quantity of apples (
id:1
) isqt:4
- the quantity of bananas (
id:2
) isqt:2
- the quantity of chocolates (
id:3
) isqt:2
So by using cart, I have to obtain orders=[{id:1,qt:4},{id:2, qt:2},{id:3,qt:2}]
. It should be clear.
CodePudding user response:
Since you're looking for a "LINQ-like" solution, you probably want to use the higher order functions like map
/filter
/reduce
.
Strictly, your problem cannot be solved purely with LINQ projections. Those merely represent map
(Select
), concatMap
/flatMap
(SelectMany
), and zip
(Zip
). Your problem involves counting the occurences of each id
throughout the entire array.
Pretty much every data manipulation problem can be solved with higher order folds, i.e reduce
in javascript land, Aggregate
in C# land. This one is no exception. The first thing to do, is to count the occurrences of each id
, and build a counter object.
cart.reduce((acc, { id }) => {
acc[id] = (acc[id] ?? 0) 1;
return acc;
}, {} as Record<number, number>);
Essentially, you start the fold operation with an empty object, then add each id
and its occurrence count. Every time an id
is encountered in the cart
array, you increment its count in the object. If the id
doesn't already exist in the accumulating object, nullish coalescing (acc[id] ?? 0
) uses 0
and increments that instead.
This will give you-
{ '1': 4, '2': 2, '3': 2 }
Now, you need to turn this into-
[ { id: 1, qt: 4 }, { id: 2, qt: 2 }, { id: 3, qt: 2 } ]
For that, use Object.entries
on the fold result to get-
> Object.entries({ '1': 4, '2': 2, '3': 2 })
[ [ '1', 4 ], [ '2', 2 ], [ '3', 2 ] ]
Finally, a simple map
is all you need-
Object.entries(...).map(([id, qt]) => ({ id: Number(id), qt }))
Combining all that, you have-
export const orders: Order[] = Object.entries(
cart.reduce((acc, { id }) => {
acc[id] = (acc[id] ?? 0) 1;
return acc;
}, {} as Record<number, number>)
).map(([id, qt]) => ({ id: Number(id), qt }));
One thing to note here is that Object.entries
is pretty inefficient since it builds up an array instead of an iterator. If you're into efficiency, roll an iterator version of Object.entries
and use that instead, using generator functions-
function* objEntries<T>(x: Record<string, T>): IterableIterator<[string, T]> {
for (const k in x) {
yield [k, x[k]];
}
}