cyucelen / marker

🖍️ Marker is the easiest way to match and mark strings for colorful terminal outputs!
MIT License
47 stars 13 forks source link

Matching days of the week #3

Closed Srivats1991 closed 4 years ago

Srivats1991 commented 4 years ago

Added a new matcher api to match days of the week in a string. The string can have days starting with a uppercase (starting after a period) or completely lower case. A new test case has been added as well.

Closes #11

cyucelen commented 4 years ago

Hey, i see your message that you are new to Golang. I would like help you with writing some reviews iteratively to make your pull request ready to merge!

1. First of all you don't need to specify size of the string slice. Simply,

// use this notation
daysOfWeek := []string{"monday", "Monday", "tuesday", "Tuesday", "wednesday", "Wednesday", "thursday", "Thursday", "friday", "Friday", "saturday", "Saturday", "sunday", "Sunday"}

// or this notation if you want an array
daysOfWeek := [14]string{"monday", "Monday", "tuesday", "Tuesday", "wednesday", "Wednesday", "thursday", "Thursday", "friday", "Friday", "saturday", "Saturday", "sunday", "Sunday"}

// instead of this
var days [14]string = [14]string {"monday", "Monday", "tuesday", "Tuesday", "wednesday", "Wednesday", "thursday", "Thursday", "friday", "Friday", "saturday", "Saturday", "sunday", "Sunday"}

Also since these are constant values to use, we don't need to allocate them all over again when this matcher called. So we can define it outside function as const, but in Golang only numbers, strings or booleans can be defined as constants (because they can be evaluated at compile-time, slices can not).

var daysOfWeek = [14]string{
    "monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday",
    "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday",
}
// MatchDaysOfWeek returns a MatcherFunc that matches days of the week in given string
func MatchDaysOfWeek() MatcherFunc {
...

2. Since str is passed by value to the function, there is no need to copy str to newString for preventing mutation of string that is passed to the function.

3. Your matcher implementation wrong due to wrong order of patterns. In order to colorizing patterns in correct order at Mark function, patterns should be at the same order where they been in given string. For example:

str := "Today is Tuesday or tuesday not tUeSday but Tuesday"
actualMatch := MatchDaysofWeek()(str)

actualMatch.Patterns should resemble []string{"Tuesday", "tuesday", "Tuesday"} but in your implementation it looks like []string{"tuesday", "Tuesday"}.

Where is the problem?

Happy Hacktoberfest!

codecov-io commented 4 years ago

Codecov Report

Merging #3 into master will not change coverage. The diff coverage is 100%.

Impacted file tree graph

@@          Coverage Diff          @@
##           master     #3   +/-   ##
=====================================
  Coverage     100%   100%           
=====================================
  Files           3      3           
  Lines          58     76   +18     
=====================================
+ Hits           58     76   +18
Impacted Files Coverage Δ
matcher.go 100% <100%> (ø) :arrow_up:

Continue to review full report at Codecov.

Legend - Click here to learn more Δ = absolute <relative> (impact), ø = not affected, ? = missing data Powered by Codecov. Last update 984a299...46c8f39. Read the comment docs.

Srivats1991 commented 4 years ago

First of all, Thanks a lot !!!! for taking the time to write up in detail. I've made the edits and amended the request by fixing the error in the implementation itself for not taking the orderly matches. Let me know if any additional changes required. Sorry for the trouble. My golang code is still rusty. Hope to get better soon.

cyucelen commented 4 years ago

Hey @Srivats1991 , thanks for your interest again. Lets check out your new commits!

First of all, I wanted you to fix the implementation for make it work with Marker logic correctly and you did a good job as I see, but i want to point out something important. When I look for the tests, saw that you didn't add the test cases for this. So, I want to list some important steps can help you while implementing and committing a feature or fix.

1. Write a new failing test case for the new feature or fix that you are going to implement. 2. Implement your feature or fix just enough for passing the test. Nothing more nothing less. 3. Refactor. Make your code pretty and readable.

You need to keep doing these 3 steps iteratively until you achieve your fully tested implementation. When you regard these steps while writing some code, that makes your code maintainable(that's the point most of the time, because lots of developers have to read, understand and change your code).

So lets review your new commit.

1. Add the new test case and run the test to see it fails.

str := "Today is Tuesday or tuesday not tUesday but Tuesday"
actualMatch := MatchDaysofWeek()(str)
expectedMatch := Match{Template: "Today is %s or %s not tUesday but %s", Patterns: []string{"Tuesday", "tuesday", "Tuesday"}}
assert.Equal(t, expectedMatch, actualMatch)

2. Write the minimal code that can pass the test. You already wrote the implementation that passes this test, so I want to give you some feedback with refactoring.

3. Refactor I want to recommend some variable naming changes firstly, because this improves readability significantly. Open your code aside to track better these part.

var pattern []string // easy to read

> Programs must be written for people to read, and only incidentally for machines to execute.”—Abelson and Sussman 

So I hope you got my point. Let's make your code more efficient and also more readable with some more refactoring.

In this part you are checking if given string contains the day before you processing. If it is, then you are counting and replacing them etc.

// Your implementation with naming fixes applied for _, day := range daysOfWeek { if strings.Contains(str, day) { count := strings.Count(str, day) for i := 1; i <= count; i++ { matchIndex := strings.Index(str, day) str = strings.Replace(str, day, "%s", 1) patternMatchIndexes[matchIndex] = day } } }

Lets consider this:

for _, day := range daysOfWeek { for strings.Contains(str, day) { matchIndex := strings.Index(str, day) str = strings.Replace(str, day, "%s", 1) patternMatchIndexes[matchIndex] = day } }

Since we are replacing matches with `%s` one by one, we can loop until there is no match and we dont need to call `strings.Count` to calculate how many matches are there for looping that much. By this way we decreased our function's cyclomatic complexity and our function is more readable now.

* Name your `keys` slice that stores the keys of `patternMatchIndexes` map as `matchIndexes`. Also, we know the length of `patternMatchIndexes`, by providing some capacity while creating a slice, we can avoid from some extra memory allocations. If you don't specify any capacity or length, it creates a slice with double capacity and copies all elements every time it need some extra space for appending.

Some examples to creating slice with initial capacity.

matchIndexes := make([]int, 0, len(patternMatchIndexes)) patterns := make([]string, 0, len(patternMatchIndexes))



* Lastly, correct the function name to MatchDays**O**fWeek.

Waiting for your next commit. Have fun! 
Srivats1991 commented 4 years ago

Hey, could you please review the changes and move forward with the merge?