Find the fastest run of each year from a list of running results

Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
7
down vote
favorite
Problem Explained:
Imagine there is a list of running results of 1000m, like following:
[
id: 1, date: '2017-01-01 00:00:00', duration: 195 ,
id: 2, date: '2017-01-10 00:00:00', duration: 270 ,
id: 3, date: '2017-03-12 00:00:00', duration: 220 ,
id: 4, date: '2018-01-10 00:00:00', duration: 218 ,
id: 5, date: '2018-02-23 00:00:00', duration: 220 ,
id: 6, date: '2018-05-18 00:00:00', duration: 215
]
I'd like to find out the fastest run of each year, which will be
[
id: 1, date: '2017-01-01 00:00:00', duration: 195 ,
id: 6, date: '2018-05-18 00:00:00', duration: 215
]
Here's my ugly solution (TypeScript):
const items = JSON.parse('Use the JSON above.');
const results: any = ;
const fastestOfEachYear: object = ;
const idsOfFastestEachYear: object = ;
// Go through the list to find the fastest of each year.
// Save the IDs in an object.
items.forEach((item) => );
// Retrieve the IDs of the runs that are the fastest of a year.
const idsOfFastest = Object.keys(idsOfFastestEachYear).map((key) => idsOfFastestEachYear[key]);
// Loop through the list again to find those items matching the IDs
// Save them to results.
items.forEach((item) =>
const runId = item['id'];
if ($.inArray(runId, idsOfFastest) !== -1)
results.push(item);
);
Surely there are nicer and better ways of achieving this? (Reviews in TypeScript, ES6 or even just Vanilla JS are ok.)
datetime statistics typescript
add a comment |Â
up vote
7
down vote
favorite
Problem Explained:
Imagine there is a list of running results of 1000m, like following:
[
id: 1, date: '2017-01-01 00:00:00', duration: 195 ,
id: 2, date: '2017-01-10 00:00:00', duration: 270 ,
id: 3, date: '2017-03-12 00:00:00', duration: 220 ,
id: 4, date: '2018-01-10 00:00:00', duration: 218 ,
id: 5, date: '2018-02-23 00:00:00', duration: 220 ,
id: 6, date: '2018-05-18 00:00:00', duration: 215
]
I'd like to find out the fastest run of each year, which will be
[
id: 1, date: '2017-01-01 00:00:00', duration: 195 ,
id: 6, date: '2018-05-18 00:00:00', duration: 215
]
Here's my ugly solution (TypeScript):
const items = JSON.parse('Use the JSON above.');
const results: any = ;
const fastestOfEachYear: object = ;
const idsOfFastestEachYear: object = ;
// Go through the list to find the fastest of each year.
// Save the IDs in an object.
items.forEach((item) => );
// Retrieve the IDs of the runs that are the fastest of a year.
const idsOfFastest = Object.keys(idsOfFastestEachYear).map((key) => idsOfFastestEachYear[key]);
// Loop through the list again to find those items matching the IDs
// Save them to results.
items.forEach((item) =>
const runId = item['id'];
if ($.inArray(runId, idsOfFastest) !== -1)
results.push(item);
);
Surely there are nicer and better ways of achieving this? (Reviews in TypeScript, ES6 or even just Vanilla JS are ok.)
datetime statistics typescript
Are you interested in solutions using lodash/underscore or similar? It's so much easier with good abstractions at hand.
â tokland
Jun 10 at 18:35
@tokland: Yes, I thought about using some handy methods from lodash, but I struggled to introduce lodash into my Rails + webpacker + TypeScript project. But yes, a lodash solution would be acceptable too!
â Yi Zeng
Jun 11 at 2:37
add a comment |Â
up vote
7
down vote
favorite
up vote
7
down vote
favorite
Problem Explained:
Imagine there is a list of running results of 1000m, like following:
[
id: 1, date: '2017-01-01 00:00:00', duration: 195 ,
id: 2, date: '2017-01-10 00:00:00', duration: 270 ,
id: 3, date: '2017-03-12 00:00:00', duration: 220 ,
id: 4, date: '2018-01-10 00:00:00', duration: 218 ,
id: 5, date: '2018-02-23 00:00:00', duration: 220 ,
id: 6, date: '2018-05-18 00:00:00', duration: 215
]
I'd like to find out the fastest run of each year, which will be
[
id: 1, date: '2017-01-01 00:00:00', duration: 195 ,
id: 6, date: '2018-05-18 00:00:00', duration: 215
]
Here's my ugly solution (TypeScript):
const items = JSON.parse('Use the JSON above.');
const results: any = ;
const fastestOfEachYear: object = ;
const idsOfFastestEachYear: object = ;
// Go through the list to find the fastest of each year.
// Save the IDs in an object.
items.forEach((item) => );
// Retrieve the IDs of the runs that are the fastest of a year.
const idsOfFastest = Object.keys(idsOfFastestEachYear).map((key) => idsOfFastestEachYear[key]);
// Loop through the list again to find those items matching the IDs
// Save them to results.
items.forEach((item) =>
const runId = item['id'];
if ($.inArray(runId, idsOfFastest) !== -1)
results.push(item);
);
Surely there are nicer and better ways of achieving this? (Reviews in TypeScript, ES6 or even just Vanilla JS are ok.)
datetime statistics typescript
Problem Explained:
Imagine there is a list of running results of 1000m, like following:
[
id: 1, date: '2017-01-01 00:00:00', duration: 195 ,
id: 2, date: '2017-01-10 00:00:00', duration: 270 ,
id: 3, date: '2017-03-12 00:00:00', duration: 220 ,
id: 4, date: '2018-01-10 00:00:00', duration: 218 ,
id: 5, date: '2018-02-23 00:00:00', duration: 220 ,
id: 6, date: '2018-05-18 00:00:00', duration: 215
]
I'd like to find out the fastest run of each year, which will be
[
id: 1, date: '2017-01-01 00:00:00', duration: 195 ,
id: 6, date: '2018-05-18 00:00:00', duration: 215
]
Here's my ugly solution (TypeScript):
const items = JSON.parse('Use the JSON above.');
const results: any = ;
const fastestOfEachYear: object = ;
const idsOfFastestEachYear: object = ;
// Go through the list to find the fastest of each year.
// Save the IDs in an object.
items.forEach((item) => );
// Retrieve the IDs of the runs that are the fastest of a year.
const idsOfFastest = Object.keys(idsOfFastestEachYear).map((key) => idsOfFastestEachYear[key]);
// Loop through the list again to find those items matching the IDs
// Save them to results.
items.forEach((item) =>
const runId = item['id'];
if ($.inArray(runId, idsOfFastest) !== -1)
results.push(item);
);
Surely there are nicer and better ways of achieving this? (Reviews in TypeScript, ES6 or even just Vanilla JS are ok.)
datetime statistics typescript
edited Jun 15 at 16:46
200_success
123k14143399
123k14143399
asked Jun 10 at 9:26
Yi Zeng
1415
1415
Are you interested in solutions using lodash/underscore or similar? It's so much easier with good abstractions at hand.
â tokland
Jun 10 at 18:35
@tokland: Yes, I thought about using some handy methods from lodash, but I struggled to introduce lodash into my Rails + webpacker + TypeScript project. But yes, a lodash solution would be acceptable too!
â Yi Zeng
Jun 11 at 2:37
add a comment |Â
Are you interested in solutions using lodash/underscore or similar? It's so much easier with good abstractions at hand.
â tokland
Jun 10 at 18:35
@tokland: Yes, I thought about using some handy methods from lodash, but I struggled to introduce lodash into my Rails + webpacker + TypeScript project. But yes, a lodash solution would be acceptable too!
â Yi Zeng
Jun 11 at 2:37
Are you interested in solutions using lodash/underscore or similar? It's so much easier with good abstractions at hand.
â tokland
Jun 10 at 18:35
Are you interested in solutions using lodash/underscore or similar? It's so much easier with good abstractions at hand.
â tokland
Jun 10 at 18:35
@tokland: Yes, I thought about using some handy methods from lodash, but I struggled to introduce lodash into my Rails + webpacker + TypeScript project. But yes, a lodash solution would be acceptable too!
â Yi Zeng
Jun 11 at 2:37
@tokland: Yes, I thought about using some handy methods from lodash, but I struggled to introduce lodash into my Rails + webpacker + TypeScript project. But yes, a lodash solution would be acceptable too!
â Yi Zeng
Jun 11 at 2:37
add a comment |Â
3 Answers
3
active
oldest
votes
up vote
6
down vote
accepted
First solution using .forEach, for...in and .sort
Here what I did to achieve this:
First sort your array of objects, i.e convert:
data = [ ..., ..., ..., ..., ..., ... ]into an object sorted by year:
sortedY = 2017: [..., ..., ...], 2018: [..., ..., ...]You can achieve such result by using a
forEachloop:const sortedY = ;
data.forEach(e =>
const year = e.date.split('-')[0];
sortedY[year] = sortedY[year] );Then we will need to sort each object
[..., ..., ...]peryearby duration.sortedis not an array. We can't usemapunfortunately: use afor...inloop instead. However to sort each key/value (<=> year/array of results for this year) we can use one ofArray's methods:sort, the sorting criteria is the following:(a,b) => a.duration > b.durationWhere a and b are two different races from the same year.
So with:
sortedY[year].sort((a,b) => a.duration > b.duration)You get an array of sorted races for the year
year.The last thing to do is saving the first of the sorted array (the first is the one with the shortest
duration, so select it with[0]and add it toresult:result[year] = sortedY[year].sort((a,b) => a.duration > b.duration)[0]
The final code is:
const sortedY = ;
data.forEach(e =>
const year = e.date.split('-')[0];
sortedY[year] = sortedY[year] );
const result = ;
for(year in sortedY)
result[year] = sortedY[year].sort((a,b) => a.duration > b.duration)[0];
console.log(result);<script>
const data = [ id: 1, date: '2017-01-01 00:00:00', duration: 195 , id: 2, date: '2017-01-10 00:00:00', duration: 270 , id: 3, date: '2017-03-12 00:00:00', duration: 220 , id: 4, date: '2018-01-10 00:00:00', duration: 218 , id: 5, date: '2018-02-23 00:00:00', duration: 220 , id: 6, date: '2018-05-18 00:00:00', duration: 215 ]
</script>Second solution using only .forEach
I realised there was a quicker way of doing this: we don't need to loop over the array data twice! One loop will suffice. Indeed, we'll fill result as we navigate through data only replacing a yearly race if a race is shorter in duration.
So a race will be added to result[year] only if:
!result[year] || (result[year] && e.duration < result[year].duration)
is true, i.e. if either:
there is no race registered for the year
yearthere is one but its duration is longer than the one that we're checking
So the code is shorter, simpler and I bet it will be quicker: its complexity is $O(n)$. The previous code was cool but not so much efficient. I was looping twice the data: first time for the sorting, second on the sorted array. Not even mentioning that I was using sort...
let result = ;
data.forEach(e =>
const year = e.date.split('-')[0];
if( !result[year] )
console.log(result);<script>
const data = [ id: 1, date: '2017-01-01 00:00:00', duration: 195 , id: 2, date: '2017-01-10 00:00:00', duration: 270 , id: 3, date: '2017-03-12 00:00:00', duration: 220 , id: 4, date: '2018-01-10 00:00:00', duration: 218 , id: 5, date: '2018-02-23 00:00:00', duration: 220 , id: 6, date: '2018-05-18 00:00:00', duration: 215 ]
</script>
2
Very elegant solution. Thanks! I'll accept once I've got a chance to try it out.
â Yi Zeng
Jun 11 at 7:26
add a comment |Â
up vote
3
down vote
@Ivan has said enough already, so I'll allow myself to include only code that will give you exactly the same output that you expected in your question.
It works just like @Ivan's second solution, except that it uses .reduce() and gives you only values of the same resultant object as his, which is what you were looking for. $O(n)$ complexity.
const getFastestRuns = input => Object.values(input.reduce((acc, curr) => , ));
/* DEMO */
const input = [
id: 1, date: '2017-01-01 00:00:00', duration: 195 ,
id: 2, date: '2017-01-10 00:00:00', duration: 270 ,
id: 3, date: '2017-03-12 00:00:00', duration: 220 ,
id: 4, date: '2018-01-10 00:00:00', duration: 218 ,
id: 5, date: '2018-02-23 00:00:00', duration: 220 ,
id: 6, date: '2018-05-18 00:00:00', duration: 215
];
console.log(
JSON.stringify(
getFastestRuns(input)
)
);
1
Nice elegant solution. Thanks!
â Yi Zeng
Jun 11 at 7:27
1
Nice, I tried using only.reducein my second attempt but failed. I didn't know the method could takeinitialValueas [the second argument]. Thanks for sharing +1.
â Ivan
Jun 11 at 12:28
add a comment |Â
up vote
1
down vote
The problem is that you are kind of re-implenting abstractions (groupBy, min, ...) found in utility libraries like lodash, underscore, ramda... So my advice would be to just use one of these and write declarative/functional code. Something like this:
const _ = require('lodash');
const getYear = run => parseInt(run.date.split("-")[0]);
const fastestRunByYear = _(runs)
.groupBy(getYear)
.map(runsForYear => _.minBy(runsForYear, "duration"))
.value();
If not using external libraries was a requirement, for whatever reason, then I'd implement groupBy and minBy as a separate functions and use the same code above (without _ wrappings, of course). Note that I could move the getYearcode into the map, but I create a separate function to make it more clear.
Thanks! Yes, now I have Lodash in my project.
â Yi Zeng
Jun 14 at 1:19
add a comment |Â
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
6
down vote
accepted
First solution using .forEach, for...in and .sort
Here what I did to achieve this:
First sort your array of objects, i.e convert:
data = [ ..., ..., ..., ..., ..., ... ]into an object sorted by year:
sortedY = 2017: [..., ..., ...], 2018: [..., ..., ...]You can achieve such result by using a
forEachloop:const sortedY = ;
data.forEach(e =>
const year = e.date.split('-')[0];
sortedY[year] = sortedY[year] );Then we will need to sort each object
[..., ..., ...]peryearby duration.sortedis not an array. We can't usemapunfortunately: use afor...inloop instead. However to sort each key/value (<=> year/array of results for this year) we can use one ofArray's methods:sort, the sorting criteria is the following:(a,b) => a.duration > b.durationWhere a and b are two different races from the same year.
So with:
sortedY[year].sort((a,b) => a.duration > b.duration)You get an array of sorted races for the year
year.The last thing to do is saving the first of the sorted array (the first is the one with the shortest
duration, so select it with[0]and add it toresult:result[year] = sortedY[year].sort((a,b) => a.duration > b.duration)[0]
The final code is:
const sortedY = ;
data.forEach(e =>
const year = e.date.split('-')[0];
sortedY[year] = sortedY[year] );
const result = ;
for(year in sortedY)
result[year] = sortedY[year].sort((a,b) => a.duration > b.duration)[0];
console.log(result);<script>
const data = [ id: 1, date: '2017-01-01 00:00:00', duration: 195 , id: 2, date: '2017-01-10 00:00:00', duration: 270 , id: 3, date: '2017-03-12 00:00:00', duration: 220 , id: 4, date: '2018-01-10 00:00:00', duration: 218 , id: 5, date: '2018-02-23 00:00:00', duration: 220 , id: 6, date: '2018-05-18 00:00:00', duration: 215 ]
</script>Second solution using only .forEach
I realised there was a quicker way of doing this: we don't need to loop over the array data twice! One loop will suffice. Indeed, we'll fill result as we navigate through data only replacing a yearly race if a race is shorter in duration.
So a race will be added to result[year] only if:
!result[year] || (result[year] && e.duration < result[year].duration)
is true, i.e. if either:
there is no race registered for the year
yearthere is one but its duration is longer than the one that we're checking
So the code is shorter, simpler and I bet it will be quicker: its complexity is $O(n)$. The previous code was cool but not so much efficient. I was looping twice the data: first time for the sorting, second on the sorted array. Not even mentioning that I was using sort...
let result = ;
data.forEach(e =>
const year = e.date.split('-')[0];
if( !result[year] )
console.log(result);<script>
const data = [ id: 1, date: '2017-01-01 00:00:00', duration: 195 , id: 2, date: '2017-01-10 00:00:00', duration: 270 , id: 3, date: '2017-03-12 00:00:00', duration: 220 , id: 4, date: '2018-01-10 00:00:00', duration: 218 , id: 5, date: '2018-02-23 00:00:00', duration: 220 , id: 6, date: '2018-05-18 00:00:00', duration: 215 ]
</script>
2
Very elegant solution. Thanks! I'll accept once I've got a chance to try it out.
â Yi Zeng
Jun 11 at 7:26
add a comment |Â
up vote
6
down vote
accepted
First solution using .forEach, for...in and .sort
Here what I did to achieve this:
First sort your array of objects, i.e convert:
data = [ ..., ..., ..., ..., ..., ... ]into an object sorted by year:
sortedY = 2017: [..., ..., ...], 2018: [..., ..., ...]You can achieve such result by using a
forEachloop:const sortedY = ;
data.forEach(e =>
const year = e.date.split('-')[0];
sortedY[year] = sortedY[year] );Then we will need to sort each object
[..., ..., ...]peryearby duration.sortedis not an array. We can't usemapunfortunately: use afor...inloop instead. However to sort each key/value (<=> year/array of results for this year) we can use one ofArray's methods:sort, the sorting criteria is the following:(a,b) => a.duration > b.durationWhere a and b are two different races from the same year.
So with:
sortedY[year].sort((a,b) => a.duration > b.duration)You get an array of sorted races for the year
year.The last thing to do is saving the first of the sorted array (the first is the one with the shortest
duration, so select it with[0]and add it toresult:result[year] = sortedY[year].sort((a,b) => a.duration > b.duration)[0]
The final code is:
const sortedY = ;
data.forEach(e =>
const year = e.date.split('-')[0];
sortedY[year] = sortedY[year] );
const result = ;
for(year in sortedY)
result[year] = sortedY[year].sort((a,b) => a.duration > b.duration)[0];
console.log(result);<script>
const data = [ id: 1, date: '2017-01-01 00:00:00', duration: 195 , id: 2, date: '2017-01-10 00:00:00', duration: 270 , id: 3, date: '2017-03-12 00:00:00', duration: 220 , id: 4, date: '2018-01-10 00:00:00', duration: 218 , id: 5, date: '2018-02-23 00:00:00', duration: 220 , id: 6, date: '2018-05-18 00:00:00', duration: 215 ]
</script>Second solution using only .forEach
I realised there was a quicker way of doing this: we don't need to loop over the array data twice! One loop will suffice. Indeed, we'll fill result as we navigate through data only replacing a yearly race if a race is shorter in duration.
So a race will be added to result[year] only if:
!result[year] || (result[year] && e.duration < result[year].duration)
is true, i.e. if either:
there is no race registered for the year
yearthere is one but its duration is longer than the one that we're checking
So the code is shorter, simpler and I bet it will be quicker: its complexity is $O(n)$. The previous code was cool but not so much efficient. I was looping twice the data: first time for the sorting, second on the sorted array. Not even mentioning that I was using sort...
let result = ;
data.forEach(e =>
const year = e.date.split('-')[0];
if( !result[year] )
console.log(result);<script>
const data = [ id: 1, date: '2017-01-01 00:00:00', duration: 195 , id: 2, date: '2017-01-10 00:00:00', duration: 270 , id: 3, date: '2017-03-12 00:00:00', duration: 220 , id: 4, date: '2018-01-10 00:00:00', duration: 218 , id: 5, date: '2018-02-23 00:00:00', duration: 220 , id: 6, date: '2018-05-18 00:00:00', duration: 215 ]
</script>
2
Very elegant solution. Thanks! I'll accept once I've got a chance to try it out.
â Yi Zeng
Jun 11 at 7:26
add a comment |Â
up vote
6
down vote
accepted
up vote
6
down vote
accepted
First solution using .forEach, for...in and .sort
Here what I did to achieve this:
First sort your array of objects, i.e convert:
data = [ ..., ..., ..., ..., ..., ... ]into an object sorted by year:
sortedY = 2017: [..., ..., ...], 2018: [..., ..., ...]You can achieve such result by using a
forEachloop:const sortedY = ;
data.forEach(e =>
const year = e.date.split('-')[0];
sortedY[year] = sortedY[year] );Then we will need to sort each object
[..., ..., ...]peryearby duration.sortedis not an array. We can't usemapunfortunately: use afor...inloop instead. However to sort each key/value (<=> year/array of results for this year) we can use one ofArray's methods:sort, the sorting criteria is the following:(a,b) => a.duration > b.durationWhere a and b are two different races from the same year.
So with:
sortedY[year].sort((a,b) => a.duration > b.duration)You get an array of sorted races for the year
year.The last thing to do is saving the first of the sorted array (the first is the one with the shortest
duration, so select it with[0]and add it toresult:result[year] = sortedY[year].sort((a,b) => a.duration > b.duration)[0]
The final code is:
const sortedY = ;
data.forEach(e =>
const year = e.date.split('-')[0];
sortedY[year] = sortedY[year] );
const result = ;
for(year in sortedY)
result[year] = sortedY[year].sort((a,b) => a.duration > b.duration)[0];
console.log(result);<script>
const data = [ id: 1, date: '2017-01-01 00:00:00', duration: 195 , id: 2, date: '2017-01-10 00:00:00', duration: 270 , id: 3, date: '2017-03-12 00:00:00', duration: 220 , id: 4, date: '2018-01-10 00:00:00', duration: 218 , id: 5, date: '2018-02-23 00:00:00', duration: 220 , id: 6, date: '2018-05-18 00:00:00', duration: 215 ]
</script>Second solution using only .forEach
I realised there was a quicker way of doing this: we don't need to loop over the array data twice! One loop will suffice. Indeed, we'll fill result as we navigate through data only replacing a yearly race if a race is shorter in duration.
So a race will be added to result[year] only if:
!result[year] || (result[year] && e.duration < result[year].duration)
is true, i.e. if either:
there is no race registered for the year
yearthere is one but its duration is longer than the one that we're checking
So the code is shorter, simpler and I bet it will be quicker: its complexity is $O(n)$. The previous code was cool but not so much efficient. I was looping twice the data: first time for the sorting, second on the sorted array. Not even mentioning that I was using sort...
let result = ;
data.forEach(e =>
const year = e.date.split('-')[0];
if( !result[year] )
console.log(result);<script>
const data = [ id: 1, date: '2017-01-01 00:00:00', duration: 195 , id: 2, date: '2017-01-10 00:00:00', duration: 270 , id: 3, date: '2017-03-12 00:00:00', duration: 220 , id: 4, date: '2018-01-10 00:00:00', duration: 218 , id: 5, date: '2018-02-23 00:00:00', duration: 220 , id: 6, date: '2018-05-18 00:00:00', duration: 215 ]
</script>First solution using .forEach, for...in and .sort
Here what I did to achieve this:
First sort your array of objects, i.e convert:
data = [ ..., ..., ..., ..., ..., ... ]into an object sorted by year:
sortedY = 2017: [..., ..., ...], 2018: [..., ..., ...]You can achieve such result by using a
forEachloop:const sortedY = ;
data.forEach(e =>
const year = e.date.split('-')[0];
sortedY[year] = sortedY[year] );Then we will need to sort each object
[..., ..., ...]peryearby duration.sortedis not an array. We can't usemapunfortunately: use afor...inloop instead. However to sort each key/value (<=> year/array of results for this year) we can use one ofArray's methods:sort, the sorting criteria is the following:(a,b) => a.duration > b.durationWhere a and b are two different races from the same year.
So with:
sortedY[year].sort((a,b) => a.duration > b.duration)You get an array of sorted races for the year
year.The last thing to do is saving the first of the sorted array (the first is the one with the shortest
duration, so select it with[0]and add it toresult:result[year] = sortedY[year].sort((a,b) => a.duration > b.duration)[0]
The final code is:
const sortedY = ;
data.forEach(e =>
const year = e.date.split('-')[0];
sortedY[year] = sortedY[year] );
const result = ;
for(year in sortedY)
result[year] = sortedY[year].sort((a,b) => a.duration > b.duration)[0];
console.log(result);<script>
const data = [ id: 1, date: '2017-01-01 00:00:00', duration: 195 , id: 2, date: '2017-01-10 00:00:00', duration: 270 , id: 3, date: '2017-03-12 00:00:00', duration: 220 , id: 4, date: '2018-01-10 00:00:00', duration: 218 , id: 5, date: '2018-02-23 00:00:00', duration: 220 , id: 6, date: '2018-05-18 00:00:00', duration: 215 ]
</script>Second solution using only .forEach
I realised there was a quicker way of doing this: we don't need to loop over the array data twice! One loop will suffice. Indeed, we'll fill result as we navigate through data only replacing a yearly race if a race is shorter in duration.
So a race will be added to result[year] only if:
!result[year] || (result[year] && e.duration < result[year].duration)
is true, i.e. if either:
there is no race registered for the year
yearthere is one but its duration is longer than the one that we're checking
So the code is shorter, simpler and I bet it will be quicker: its complexity is $O(n)$. The previous code was cool but not so much efficient. I was looping twice the data: first time for the sorting, second on the sorted array. Not even mentioning that I was using sort...
let result = ;
data.forEach(e =>
const year = e.date.split('-')[0];
if( !result[year] )
console.log(result);<script>
const data = [ id: 1, date: '2017-01-01 00:00:00', duration: 195 , id: 2, date: '2017-01-10 00:00:00', duration: 270 , id: 3, date: '2017-03-12 00:00:00', duration: 220 , id: 4, date: '2018-01-10 00:00:00', duration: 218 , id: 5, date: '2018-02-23 00:00:00', duration: 220 , id: 6, date: '2018-05-18 00:00:00', duration: 215 ]
</script>const sortedY = ;
data.forEach(e =>
const year = e.date.split('-')[0];
sortedY[year] = sortedY[year] );
const result = ;
for(year in sortedY)
result[year] = sortedY[year].sort((a,b) => a.duration > b.duration)[0];
console.log(result);<script>
const data = [ id: 1, date: '2017-01-01 00:00:00', duration: 195 , id: 2, date: '2017-01-10 00:00:00', duration: 270 , id: 3, date: '2017-03-12 00:00:00', duration: 220 , id: 4, date: '2018-01-10 00:00:00', duration: 218 , id: 5, date: '2018-02-23 00:00:00', duration: 220 , id: 6, date: '2018-05-18 00:00:00', duration: 215 ]
</script>const sortedY = ;
data.forEach(e =>
const year = e.date.split('-')[0];
sortedY[year] = sortedY[year] );
const result = ;
for(year in sortedY)
result[year] = sortedY[year].sort((a,b) => a.duration > b.duration)[0];
console.log(result);<script>
const data = [ id: 1, date: '2017-01-01 00:00:00', duration: 195 , id: 2, date: '2017-01-10 00:00:00', duration: 270 , id: 3, date: '2017-03-12 00:00:00', duration: 220 , id: 4, date: '2018-01-10 00:00:00', duration: 218 , id: 5, date: '2018-02-23 00:00:00', duration: 220 , id: 6, date: '2018-05-18 00:00:00', duration: 215 ]
</script>let result = ;
data.forEach(e =>
const year = e.date.split('-')[0];
if( !result[year] )
console.log(result);<script>
const data = [ id: 1, date: '2017-01-01 00:00:00', duration: 195 , id: 2, date: '2017-01-10 00:00:00', duration: 270 , id: 3, date: '2017-03-12 00:00:00', duration: 220 , id: 4, date: '2018-01-10 00:00:00', duration: 218 , id: 5, date: '2018-02-23 00:00:00', duration: 220 , id: 6, date: '2018-05-18 00:00:00', duration: 215 ]
</script>let result = ;
data.forEach(e =>
const year = e.date.split('-')[0];
if( !result[year] )
console.log(result);<script>
const data = [ id: 1, date: '2017-01-01 00:00:00', duration: 195 , id: 2, date: '2017-01-10 00:00:00', duration: 270 , id: 3, date: '2017-03-12 00:00:00', duration: 220 , id: 4, date: '2018-01-10 00:00:00', duration: 218 , id: 5, date: '2018-02-23 00:00:00', duration: 220 , id: 6, date: '2018-05-18 00:00:00', duration: 215 ]
</script>edited Jun 15 at 14:49
answered Jun 10 at 13:05
Ivan
33511
33511
2
Very elegant solution. Thanks! I'll accept once I've got a chance to try it out.
â Yi Zeng
Jun 11 at 7:26
add a comment |Â
2
Very elegant solution. Thanks! I'll accept once I've got a chance to try it out.
â Yi Zeng
Jun 11 at 7:26
2
2
Very elegant solution. Thanks! I'll accept once I've got a chance to try it out.
â Yi Zeng
Jun 11 at 7:26
Very elegant solution. Thanks! I'll accept once I've got a chance to try it out.
â Yi Zeng
Jun 11 at 7:26
add a comment |Â
up vote
3
down vote
@Ivan has said enough already, so I'll allow myself to include only code that will give you exactly the same output that you expected in your question.
It works just like @Ivan's second solution, except that it uses .reduce() and gives you only values of the same resultant object as his, which is what you were looking for. $O(n)$ complexity.
const getFastestRuns = input => Object.values(input.reduce((acc, curr) => , ));
/* DEMO */
const input = [
id: 1, date: '2017-01-01 00:00:00', duration: 195 ,
id: 2, date: '2017-01-10 00:00:00', duration: 270 ,
id: 3, date: '2017-03-12 00:00:00', duration: 220 ,
id: 4, date: '2018-01-10 00:00:00', duration: 218 ,
id: 5, date: '2018-02-23 00:00:00', duration: 220 ,
id: 6, date: '2018-05-18 00:00:00', duration: 215
];
console.log(
JSON.stringify(
getFastestRuns(input)
)
);
1
Nice elegant solution. Thanks!
â Yi Zeng
Jun 11 at 7:27
1
Nice, I tried using only.reducein my second attempt but failed. I didn't know the method could takeinitialValueas [the second argument]. Thanks for sharing +1.
â Ivan
Jun 11 at 12:28
add a comment |Â
up vote
3
down vote
@Ivan has said enough already, so I'll allow myself to include only code that will give you exactly the same output that you expected in your question.
It works just like @Ivan's second solution, except that it uses .reduce() and gives you only values of the same resultant object as his, which is what you were looking for. $O(n)$ complexity.
const getFastestRuns = input => Object.values(input.reduce((acc, curr) => , ));
/* DEMO */
const input = [
id: 1, date: '2017-01-01 00:00:00', duration: 195 ,
id: 2, date: '2017-01-10 00:00:00', duration: 270 ,
id: 3, date: '2017-03-12 00:00:00', duration: 220 ,
id: 4, date: '2018-01-10 00:00:00', duration: 218 ,
id: 5, date: '2018-02-23 00:00:00', duration: 220 ,
id: 6, date: '2018-05-18 00:00:00', duration: 215
];
console.log(
JSON.stringify(
getFastestRuns(input)
)
);
1
Nice elegant solution. Thanks!
â Yi Zeng
Jun 11 at 7:27
1
Nice, I tried using only.reducein my second attempt but failed. I didn't know the method could takeinitialValueas [the second argument]. Thanks for sharing +1.
â Ivan
Jun 11 at 12:28
add a comment |Â
up vote
3
down vote
up vote
3
down vote
@Ivan has said enough already, so I'll allow myself to include only code that will give you exactly the same output that you expected in your question.
It works just like @Ivan's second solution, except that it uses .reduce() and gives you only values of the same resultant object as his, which is what you were looking for. $O(n)$ complexity.
const getFastestRuns = input => Object.values(input.reduce((acc, curr) => , ));
/* DEMO */
const input = [
id: 1, date: '2017-01-01 00:00:00', duration: 195 ,
id: 2, date: '2017-01-10 00:00:00', duration: 270 ,
id: 3, date: '2017-03-12 00:00:00', duration: 220 ,
id: 4, date: '2018-01-10 00:00:00', duration: 218 ,
id: 5, date: '2018-02-23 00:00:00', duration: 220 ,
id: 6, date: '2018-05-18 00:00:00', duration: 215
];
console.log(
JSON.stringify(
getFastestRuns(input)
)
);@Ivan has said enough already, so I'll allow myself to include only code that will give you exactly the same output that you expected in your question.
It works just like @Ivan's second solution, except that it uses .reduce() and gives you only values of the same resultant object as his, which is what you were looking for. $O(n)$ complexity.
const getFastestRuns = input => Object.values(input.reduce((acc, curr) => , ));
/* DEMO */
const input = [
id: 1, date: '2017-01-01 00:00:00', duration: 195 ,
id: 2, date: '2017-01-10 00:00:00', duration: 270 ,
id: 3, date: '2017-03-12 00:00:00', duration: 220 ,
id: 4, date: '2018-01-10 00:00:00', duration: 218 ,
id: 5, date: '2018-02-23 00:00:00', duration: 220 ,
id: 6, date: '2018-05-18 00:00:00', duration: 215
];
console.log(
JSON.stringify(
getFastestRuns(input)
)
);const getFastestRuns = input => Object.values(input.reduce((acc, curr) => , ));
/* DEMO */
const input = [
id: 1, date: '2017-01-01 00:00:00', duration: 195 ,
id: 2, date: '2017-01-10 00:00:00', duration: 270 ,
id: 3, date: '2017-03-12 00:00:00', duration: 220 ,
id: 4, date: '2018-01-10 00:00:00', duration: 218 ,
id: 5, date: '2018-02-23 00:00:00', duration: 220 ,
id: 6, date: '2018-05-18 00:00:00', duration: 215
];
console.log(
JSON.stringify(
getFastestRuns(input)
)
);const getFastestRuns = input => Object.values(input.reduce((acc, curr) => , ));
/* DEMO */
const input = [
id: 1, date: '2017-01-01 00:00:00', duration: 195 ,
id: 2, date: '2017-01-10 00:00:00', duration: 270 ,
id: 3, date: '2017-03-12 00:00:00', duration: 220 ,
id: 4, date: '2018-01-10 00:00:00', duration: 218 ,
id: 5, date: '2018-02-23 00:00:00', duration: 220 ,
id: 6, date: '2018-05-18 00:00:00', duration: 215
];
console.log(
JSON.stringify(
getFastestRuns(input)
)
);answered Jun 10 at 22:06
Przemek
1,032213
1,032213
1
Nice elegant solution. Thanks!
â Yi Zeng
Jun 11 at 7:27
1
Nice, I tried using only.reducein my second attempt but failed. I didn't know the method could takeinitialValueas [the second argument]. Thanks for sharing +1.
â Ivan
Jun 11 at 12:28
add a comment |Â
1
Nice elegant solution. Thanks!
â Yi Zeng
Jun 11 at 7:27
1
Nice, I tried using only.reducein my second attempt but failed. I didn't know the method could takeinitialValueas [the second argument]. Thanks for sharing +1.
â Ivan
Jun 11 at 12:28
1
1
Nice elegant solution. Thanks!
â Yi Zeng
Jun 11 at 7:27
Nice elegant solution. Thanks!
â Yi Zeng
Jun 11 at 7:27
1
1
Nice, I tried using only
.reduce in my second attempt but failed. I didn't know the method could take initialValue as [the second argument]. Thanks for sharing +1.â Ivan
Jun 11 at 12:28
Nice, I tried using only
.reduce in my second attempt but failed. I didn't know the method could take initialValue as [the second argument]. Thanks for sharing +1.â Ivan
Jun 11 at 12:28
add a comment |Â
up vote
1
down vote
The problem is that you are kind of re-implenting abstractions (groupBy, min, ...) found in utility libraries like lodash, underscore, ramda... So my advice would be to just use one of these and write declarative/functional code. Something like this:
const _ = require('lodash');
const getYear = run => parseInt(run.date.split("-")[0]);
const fastestRunByYear = _(runs)
.groupBy(getYear)
.map(runsForYear => _.minBy(runsForYear, "duration"))
.value();
If not using external libraries was a requirement, for whatever reason, then I'd implement groupBy and minBy as a separate functions and use the same code above (without _ wrappings, of course). Note that I could move the getYearcode into the map, but I create a separate function to make it more clear.
Thanks! Yes, now I have Lodash in my project.
â Yi Zeng
Jun 14 at 1:19
add a comment |Â
up vote
1
down vote
The problem is that you are kind of re-implenting abstractions (groupBy, min, ...) found in utility libraries like lodash, underscore, ramda... So my advice would be to just use one of these and write declarative/functional code. Something like this:
const _ = require('lodash');
const getYear = run => parseInt(run.date.split("-")[0]);
const fastestRunByYear = _(runs)
.groupBy(getYear)
.map(runsForYear => _.minBy(runsForYear, "duration"))
.value();
If not using external libraries was a requirement, for whatever reason, then I'd implement groupBy and minBy as a separate functions and use the same code above (without _ wrappings, of course). Note that I could move the getYearcode into the map, but I create a separate function to make it more clear.
Thanks! Yes, now I have Lodash in my project.
â Yi Zeng
Jun 14 at 1:19
add a comment |Â
up vote
1
down vote
up vote
1
down vote
The problem is that you are kind of re-implenting abstractions (groupBy, min, ...) found in utility libraries like lodash, underscore, ramda... So my advice would be to just use one of these and write declarative/functional code. Something like this:
const _ = require('lodash');
const getYear = run => parseInt(run.date.split("-")[0]);
const fastestRunByYear = _(runs)
.groupBy(getYear)
.map(runsForYear => _.minBy(runsForYear, "duration"))
.value();
If not using external libraries was a requirement, for whatever reason, then I'd implement groupBy and minBy as a separate functions and use the same code above (without _ wrappings, of course). Note that I could move the getYearcode into the map, but I create a separate function to make it more clear.
The problem is that you are kind of re-implenting abstractions (groupBy, min, ...) found in utility libraries like lodash, underscore, ramda... So my advice would be to just use one of these and write declarative/functional code. Something like this:
const _ = require('lodash');
const getYear = run => parseInt(run.date.split("-")[0]);
const fastestRunByYear = _(runs)
.groupBy(getYear)
.map(runsForYear => _.minBy(runsForYear, "duration"))
.value();
If not using external libraries was a requirement, for whatever reason, then I'd implement groupBy and minBy as a separate functions and use the same code above (without _ wrappings, of course). Note that I could move the getYearcode into the map, but I create a separate function to make it more clear.
edited Jun 11 at 8:31
answered Jun 11 at 8:04
tokland
10.7k11322
10.7k11322
Thanks! Yes, now I have Lodash in my project.
â Yi Zeng
Jun 14 at 1:19
add a comment |Â
Thanks! Yes, now I have Lodash in my project.
â Yi Zeng
Jun 14 at 1:19
Thanks! Yes, now I have Lodash in my project.
â Yi Zeng
Jun 14 at 1:19
Thanks! Yes, now I have Lodash in my project.
â Yi Zeng
Jun 14 at 1:19
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f196223%2ffind-the-fastest-run-of-each-year-from-a-list-of-running-results%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Are you interested in solutions using lodash/underscore or similar? It's so much easier with good abstractions at hand.
â tokland
Jun 10 at 18:35
@tokland: Yes, I thought about using some handy methods from lodash, but I struggled to introduce lodash into my Rails + webpacker + TypeScript project. But yes, a lodash solution would be acceptable too!
â Yi Zeng
Jun 11 at 2:37