-
Notifications
You must be signed in to change notification settings - Fork 36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
completed challenge #27
base: master
Are you sure you want to change the base?
Conversation
I realized after PR'ing that I forgot to sort the hash; it's fixed in the last commit. |
Looks good. I like that you're putting some real thought into how to structure this whole thing. When you're doing so, I'd focus your attention on separating things by their function. For instance, #self.to_word_array does both reading and parsing: those are two pretty different sorts of operations; they should probably be split into different methods. At the same time, you've got the reading function split into two different places - one to open the file, and another to read it; those should probably go together. Also, check out File.read in the docs. |
def self.count file | ||
raise 'WordCounter: no file given' unless file | ||
# use inject to create a hash of word counts | ||
to_word_array(file).inject({}) { |h,w| h[w] ? h[w] += 1 : h[w] = 1; h } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a bit hard to follow with all these methods chained together - maybe split them out? Also, more illustrative variable names (than h
and v
) will improve clarity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I purposefully chose to chain method calls to avoid the cost of unnecessary variable assignment. This is a practice that is encouraged by the developers on my current team, to avoid assigning a variable you will immediately discard.
I do agree that more verbose loop variable naming makes for more readable code though, and that I should break my two static methods into more fine-grained methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I hear you. But multi-line chaining is generally frowned upon for readability reasons. The solution is to turn the complicated parts into separate methods; then you can make a nice, brief chain that includes that method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A couple tips for making this more readable:
- Hashs can be instantiated with a default value. (
0
would be good choice here.) - If you switch from
inject
toeach_with_object
, you don't need to return the hash at the end of the block.
@mikegee and @sjreich thanks for the feedback. I honestly forgot that you could instantiate a hash with a default value, I'll definitely reactor to include that change. I was also unaware of @sjreich I also completely agree that my class methods can (and should) be broken into smaller methods which perform exactly one task. For example, you mentioned one method for reading and the other for parsing; that's a good practice with which I agree. I'll make these changes. |
When I began breaking my two static methods into smaller methods, I realized that a non-static class was the right way to solve the problem (in my opinion). I transformed my static methods into instance methods, but designed the WordCounter class so that one instance could be used to count the words of multiple files, instead of having a single instance of WordCounter locked to a single file forever. I also chose to use Overall great exercise, and I appreciate the feedback. Discussing different approaches is very insightful. |
I chose to create a static helper class to perform the actual counting. Its
count
method accepts a file as an argument and will raise a custom exception if it's absent.P.S., that bit of French was a nice twist.