Repository: vaskoz/dailycodingproblem-go (go)

Issue:
Day 263
Good morning! Here's your coding interview problem for today.

This problem was asked by Nest.

Create a basic sentence checker that takes in a stream of characters and determines whether they form valid sentences. If a sentence is valid, the program should print it out.

We can consider a sentence valid if it conforms to the following rules:

The sentence must start with a capital letter, followed by a lowercase letter or a space.
All other characters must be lowercase letters, separators `(,,;,:)` or terminal marks `(.,?,!,‽)`.
There must be a single space between each word.
The sentence must end with a terminal mark immediately following a word.

Pull request description:
Day263
Resolves #539.

Patch:
diff --git a/README.md b/README.md
index 4c3e462..9af05f1 100644
--- a/README.md
+++ b/README.md
@@ -271,6 +271,7 @@ problems from
 * [Day 257](https://github.com/vaskoz/dailycodingproblem-go/issues/529)
 * [Day 258](https://github.com/vaskoz/dailycodingproblem-go/issues/530)
 * [Day 260](https://github.com/vaskoz/dailycodingproblem-go/issues/533)
+* [Day 263](https://github.com/vaskoz/dailycodingproblem-go/issues/539)
 * [Day 266](https://github.com/vaskoz/dailycodingproblem-go/issues/542)
 * [Day 268](https://github.com/vaskoz/dailycodingproblem-go/issues/544)
 * [Day 269](https://github.com/vaskoz/dailycodingproblem-go/issues/547)
diff --git a/day263/problem.go b/day263/problem.go
new file mode 100644
index 0000000..5ec5fb6
--- /dev/null
+++ b/day263/problem.go
@@ -0,0 +1,46 @@
+package day263
+
+import "strings"
+
+// ValidSentences parses text and returns the valid sentences only
+// as defined by the problem.
+func ValidSentences(text string) []string {
+	var sentences []string
+	var start int
+	spaceDelimited := strings.Split(text, " ")
+	possibleSentence := false
+	for i, part := range spaceDelimited {
+		runes := []rune(part)
+		if part == "" || !isValid(runes) {
+			possibleSentence = false
+			continue
+		}
+		if runes[0] >= 'A' && runes[0] <= 'Z' {
+			possibleSentence = true
+			start = i
+			continue
+		}
+		if last := runes[len(runes)-1]; last == '.' || last == '!' || last == '?' {
+			if possibleSentence {
+				sentences = append(sentences, strings.Join(spaceDelimited[start:i+1], " "))
+			}
+		}
+	}
+	return sentences
+}
+
+func isValid(runes []rune) bool {
+	first := runes[0]
+	if !((first >= 'a' && first <= 'z') || (first >= 'A' && first <= 'Z')) {
+		return false
+	} else if len(runes) == 1 {
+		return true
+	}
+	for i := 1; i < len(runes)-1; i++ {
+		if char := runes[i]; char < 'a' || char > 'z' {
+			return false
+		}
+	}
+	last := runes[len(runes)-1]
+	return strings.ContainsRune(",.!?;:", last) || (last >= 'a' && last <= 'z')
+}


Test patch:
diff --git a/day263/problem_test.go b/day263/problem_test.go
new file mode 100644
index 0000000..1db1bb6
--- /dev/null
+++ b/day263/problem_test.go
@@ -0,0 +1,61 @@
+package day263
+
+import (
+	"reflect"
+	"testing"
+)
+
+// nolint
+var testcases = []struct {
+	text      string
+	sentences []string
+}{
+	{
+		"this is not valid. However, this sentence is totally valid. Is it not?",
+		[]string{
+			"However, this sentence is totally valid.",
+			"Is it not?",
+		},
+	},
+	{
+		"Numbers like 9 and extra  spaces. A bee bit me!",
+		[]string{
+			"A bee bit me!",
+		},
+	},
+	{
+		"A bee!",
+		[]string{
+			"A bee!",
+		},
+	},
+	{
+		"The colors: red, white and blue. I can not : space punctuations.",
+		[]string{
+			"The colors: red, white and blue.",
+		},
+	},
+	{
+		"I do not allow CamelCase. It is not good for anybody, right?",
+		[]string{
+			"It is not good for anybody, right?",
+		},
+	},
+}
+
+func TestValidSentences(t *testing.T) {
+	t.Parallel()
+	for _, tc := range testcases {
+		if result := ValidSentences(tc.text); !reflect.DeepEqual(result, tc.sentences) {
+			t.Errorf("Expected %v, got %v", tc.sentences, result)
+		}
+	}
+}
+
+func BenchmarkValidSentences(b *testing.B) {
+	for i := 0; i < b.N; i++ {
+		for _, tc := range testcases {
+			ValidSentences(tc.text)
+		}
+	}
+}