From 4c2df5b8cf724341d4f1142f368d0c7b6ca811c8 Mon Sep 17 00:00:00 2001 From: YerkibayevZurab Date: Wed, 31 Jul 2024 11:48:05 +0300 Subject: [PATCH 1/8] add solution for exercises1 --- exercise1/problem1/main.go | 18 +++++++++++++++++- exercise1/problem10/main.go | 27 ++++++++++++++++++++++++++- exercise1/problem2/main.go | 15 ++++++++++++++- exercise1/problem3/main.go | 19 ++++++++++++++++++- exercise1/problem4/main.go | 22 +++++++++++++++++++++- exercise1/problem5/main.go | 28 +++++++++++++++++++++++++++- exercise1/problem6/main.go | 32 +++++++++++++++++++++++++++++++- exercise1/problem7/main.go | 27 ++++++++++++++++++++++++++- exercise1/problem8/main.go | 26 +++++++++++++++++++++++++- exercise1/problem9/main.go | 32 +++++++++++++++++++++++++++++--- 10 files changed, 234 insertions(+), 12 deletions(-) diff --git a/exercise1/problem1/main.go b/exercise1/problem1/main.go index dfca465c..508e930b 100644 --- a/exercise1/problem1/main.go +++ b/exercise1/problem1/main.go @@ -1,3 +1,19 @@ package main -func addUp() {} +import ( + "fmt" +) + +func addUp(n int) int { + total := 0 + for i := 1; i <= n; i++ { + total += i + } + return total +} + +func main() { + fmt.Println(addUp(4)) + fmt.Println(addUp(13)) + fmt.Println(addUp(600)) +} diff --git a/exercise1/problem10/main.go b/exercise1/problem10/main.go index 04ec3430..de2cd6ce 100644 --- a/exercise1/problem10/main.go +++ b/exercise1/problem10/main.go @@ -1,3 +1,28 @@ package main -func sum() {} +import ( + "fmt" + "strconv" +) + +func sum(a, b string) (string, error) { + num1, err1 := strconv.Atoi(a) + if err1 != nil { + return "", fmt.Errorf("string: %s cannot be converted", a) + } + + num2, err2 := strconv.Atoi(b) + if err2 != nil { + return "", fmt.Errorf("string: %s cannot be converted", b) + } + + result := num1 + num2 + + return strconv.Itoa(result), nil +} + +func main() { + fmt.Println(sum("1", "2")) + fmt.Println(sum("10", "20")) + fmt.Println(sum("a", "2")) +} diff --git a/exercise1/problem2/main.go b/exercise1/problem2/main.go index 2ca540b8..04a57d3c 100644 --- a/exercise1/problem2/main.go +++ b/exercise1/problem2/main.go @@ -1,3 +1,16 @@ package main -func binary() {} +import ( + "fmt" + "strconv" +) + +func binary(decimal int) string { + return strconv.FormatInt(int64(decimal), 2) +} + +func main() { + fmt.Println(binary(1)) + fmt.Println(binary(5)) + fmt.Println(binary(10)) +} diff --git a/exercise1/problem3/main.go b/exercise1/problem3/main.go index d346641a..3cb3a08c 100644 --- a/exercise1/problem3/main.go +++ b/exercise1/problem3/main.go @@ -1,3 +1,20 @@ package main -func numberSquares() {} +import ( + "fmt" +) + +func numberSquares(n int) int { + sum := 0 + for k := 1; k <= n; k++ { + sum += (n - k + 1) * (n - k + 1) + } + return sum +} + +func main() { + fmt.Println(numberSquares(2)) + fmt.Println(numberSquares(3)) + fmt.Println(numberSquares(4)) + fmt.Println(numberSquares(5)) +} diff --git a/exercise1/problem4/main.go b/exercise1/problem4/main.go index 74af9044..dda4d8aa 100644 --- a/exercise1/problem4/main.go +++ b/exercise1/problem4/main.go @@ -1,3 +1,23 @@ package main -func detectWord() {} +import ( + "fmt" + "strings" +) + +func detectWord(crowd string) string { + var result strings.Builder + for _, char := range crowd { + if char >= 'a' && char <= 'z' { + result.WriteRune(char) + } + } + + return result.String() +} + +func main() { + fmt.Println(detectWord("UcUNFYGaFYFYGtNUH")) + fmt.Println(detectWord("bEEFGBuFBRrHgUHlNFYaYr")) + fmt.Println(detectWord("YFemHUFBbezFBYzFBYLleGBYEFGBMENTment")) +} diff --git a/exercise1/problem5/main.go b/exercise1/problem5/main.go index c5a804c9..abd208c8 100644 --- a/exercise1/problem5/main.go +++ b/exercise1/problem5/main.go @@ -1,3 +1,29 @@ package main -func potatoes() {} +import ( + "fmt" + "strings" +) + +func potatoes(s string) int { + count := 0 + substring := "potato" + + for { + index := strings.Index(s, substring) + if index == -1 { + break + } + count++ + s = s[index+len(substring):] + } + + return count +} + +func main() { + fmt.Println(potatoes("potato")) + fmt.Println(potatoes("potatopotato")) + fmt.Println(potatoes("potatoapple")) + fmt.Println(potatoes("potatopotatopotat")) +} diff --git a/exercise1/problem6/main.go b/exercise1/problem6/main.go index 06043890..58fe2af8 100644 --- a/exercise1/problem6/main.go +++ b/exercise1/problem6/main.go @@ -1,3 +1,33 @@ package main -func emojify() {} +import ( + "fmt" + "strings" +) + +func emojify(sentence string) string { + emojis := map[string]string{ + "smile": "🙂", + "grin": "😀", + "sad": "😥", + "mad": "😠", + } + + words := strings.Fields(sentence) + + for i, word := range words { + cleanWord := strings.Trim(word, ",.!?;:") + if emoji, exists := emojis[cleanWord]; exists { + words[i] = strings.Replace(word, cleanWord, emoji, 1) + } + } + + return strings.Join(words, " ") +} + +func main() { + fmt.Println(emojify("Make me smile")) + fmt.Println(emojify("Make me grin")) + fmt.Println(emojify("Make me sad")) + fmt.Println(emojify("I am so mad!")) +} diff --git a/exercise1/problem7/main.go b/exercise1/problem7/main.go index 57c99b5c..d18ef3e6 100644 --- a/exercise1/problem7/main.go +++ b/exercise1/problem7/main.go @@ -1,3 +1,28 @@ package main -func highestDigit() {} +import ( + "fmt" + "strconv" +) + +func highestDigit(num int) int { + numStr := strconv.Itoa(num) + + highest := 0 + + for _, char := range numStr { + digit, _ := strconv.Atoi(string(char)) + + if digit > highest { + highest = digit + } + } + + return highest +} + +func main() { + fmt.Println(highestDigit(379)) + fmt.Println(highestDigit(2)) + fmt.Println(highestDigit(377401)) +} diff --git a/exercise1/problem8/main.go b/exercise1/problem8/main.go index 97fa0dae..01bbe34c 100644 --- a/exercise1/problem8/main.go +++ b/exercise1/problem8/main.go @@ -1,3 +1,27 @@ package main -func countVowels() {} +import ( + "fmt" + "strings" +) + +func countVowels(s string) int { + vowels := "aeiou" + count := 0 + + s = strings.ToLower(s) + + for _, char := range s { + if strings.ContainsRune(vowels, char) { + count++ + } + } + + return count +} + +func main() { + fmt.Println(countVowels("Celebration")) + fmt.Println(countVowels("Palm")) + fmt.Println(countVowels("Prediction")) +} diff --git a/exercise1/problem9/main.go b/exercise1/problem9/main.go index e8c84a54..1109ce2b 100644 --- a/exercise1/problem9/main.go +++ b/exercise1/problem9/main.go @@ -1,7 +1,33 @@ package main -func bitwiseAND() {} +import ( + "fmt" +) -func bitwiseOR() {} +func toBinary(n int) string { + return fmt.Sprintf("%08b", n) +} -func bitwiseXOR() {} +func bitwiseAND(a, b int) int { + return a & b +} + +func bitwiseOR(a, b int) int { + return a | b +} + +func bitwiseXOR(a, b int) int { + return a ^ b +} + +func main() { + a := 6 + b := 23 + + fmt.Println("a =", toBinary(a)) + fmt.Println("b =", toBinary(b)) + + fmt.Println("bitwiseAND =", toBinary(bitwiseAND(a, b))) + fmt.Println("bitwiseOR =", toBinary(bitwiseOR(a, b))) + fmt.Println("bitwiseXOR =", toBinary(bitwiseXOR(a, b))) +} From 5e7e4ac854bee6d9065acd5761c86b287964688c Mon Sep 17 00:00:00 2001 From: Yerkibayev Zurab Date: Thu, 26 Sep 2024 01:47:34 +0500 Subject: [PATCH 2/8] add solution for exercises 2 --- exercise2/problem1/problem1.go | 20 +++++++++++- exercise2/problem10/problem10.go | 36 ++++++++++++++++++++- exercise2/problem11/problem11.go | 25 ++++++++++++++- exercise2/problem12/problem12.go | 36 ++++++++++++++++++++- exercise2/problem2/problem2.go | 19 ++++++++++- exercise2/problem3/problem3.go | 54 +++++++++++++++++++++++++++++++- exercise2/problem4/problem4.go | 20 +++++++++++- exercise2/problem5/problem5.go | 20 +++++++++++- exercise2/problem6/problem6.go | 27 +++++++++++++++- exercise2/problem7/problem7.go | 14 ++++++++- exercise2/problem8/problem8.go | 23 +++++++++----- exercise2/problem9/problem9.go | 24 +++++++++++++- 12 files changed, 300 insertions(+), 18 deletions(-) diff --git a/exercise2/problem1/problem1.go b/exercise2/problem1/problem1.go index 4763006c..76cff063 100644 --- a/exercise2/problem1/problem1.go +++ b/exercise2/problem1/problem1.go @@ -1,4 +1,22 @@ package problem1 -func isChangeEnough() { +import ( + "fmt" +) + +func isChangeEnough(change [4]int, amountDue float32) bool { + + totalDueCents := int(amountDue * 100) + + totalChangeCents := change[0]*25 + change[1]*10 + change[2]*5 + change[3]*1 + + return totalChangeCents >= totalDueCents +} + +func main() { + fmt.Println(isChangeEnough([4]int{2, 100, 0, 0}, 14.11)) + fmt.Println(isChangeEnough([4]int{0, 0, 20, 5}, 0.75)) + fmt.Println(isChangeEnough([4]int{30, 40, 20, 5}, 12.55)) + fmt.Println(isChangeEnough([4]int{10, 0, 0, 50}, 3.85)) + fmt.Println(isChangeEnough([4]int{1, 0, 5, 219}, 19.99)) } diff --git a/exercise2/problem10/problem10.go b/exercise2/problem10/problem10.go index 7142a022..0f510c84 100644 --- a/exercise2/problem10/problem10.go +++ b/exercise2/problem10/problem10.go @@ -1,3 +1,37 @@ package problem10 -func factory() {} +import ( + "fmt" +) + +func factory() (map[string]int, func(string) func(int) int) { + brands := make(map[string]int) + + makeBrand := func(brand string) func(int) int { + + if _, exists := brands[brand]; !exists { + brands[brand] = 0 + } + + return func(count int) int { + + brands[brand] += count + return brands[brand] + } + } + + return brands, makeBrand +} + +func main() { + brands, makeBrand := factory() + + toyotaIncrementer := makeBrand("Toyota") + toyotaIncrementer(1) + toyotaIncrementer(2) + hyundaiIncrementer := makeBrand("Hyundai") + hyundaiIncrementer(5) + makeBrand("Kia") + + fmt.Println(brands) +} diff --git a/exercise2/problem11/problem11.go b/exercise2/problem11/problem11.go index 33988711..bb54012d 100644 --- a/exercise2/problem11/problem11.go +++ b/exercise2/problem11/problem11.go @@ -1,3 +1,26 @@ package problem11 -func removeDups() {} +import ( + "fmt" +) + +func removeDups[T comparable](items []T) []T { + seen := make(map[T]bool) + result := []T{} + + for _, item := range items { + if !seen[item] { + seen[item] = true + result = append(result, item) + } + } + + return result +} + +func main() { + + fmt.Println(removeDups([]int{1, 0, 1, 0})) + fmt.Println(removeDups([]bool{true, false, false, true})) + fmt.Println(removeDups([]string{"John", "Taylor", "John"})) +} diff --git a/exercise2/problem12/problem12.go b/exercise2/problem12/problem12.go index 4c1ae327..eaee0423 100644 --- a/exercise2/problem12/problem12.go +++ b/exercise2/problem12/problem12.go @@ -1,3 +1,37 @@ package problem11 -func keysAndValues() {} +import ( + "fmt" + "sort" +) + +func keysAndValues[K comparable, V any](m map[K]V) ([]K, []V) { + keys := make([]K, 0, len(m)) + + for k := range m { + keys = append(keys, k) + } + + sort.Slice(keys, func(i, j int) bool { + return fmt.Sprintf("%v", keys[i]) < fmt.Sprintf("%v", keys[j]) + }) + + values := make([]V, len(keys)) + for i, k := range keys { + values[i] = m[k] + } + + return keys, values +} + +func main() { + + keys1, values1 := keysAndValues(map[string]int{"a": 1, "b": 2, "c": 3}) + fmt.Println(keys1, values1) + + keys2, values2 := keysAndValues(map[string]string{"a": "Apple", "b": "Microsoft", "c": "Google"}) + fmt.Println(keys2, values2) + + keys3, values3 := keysAndValues(map[int]bool{1: true, 2: false, 3: false}) + fmt.Println(keys3, values3) +} diff --git a/exercise2/problem2/problem2.go b/exercise2/problem2/problem2.go index fdb199f0..b513eddb 100644 --- a/exercise2/problem2/problem2.go +++ b/exercise2/problem2/problem2.go @@ -1,4 +1,21 @@ package problem2 -func capitalize() { +import ( + "fmt" + "strings" +) + +func capitalize(names []string) []string { + capitalizedNames := make([]string, len(names)) + for i, name := range names { + + capitalizedNames[i] = strings.Title(strings.ToLower(name)) + } + return capitalizedNames +} + +func main() { + fmt.Println(capitalize([]string{"mavis", "senaida", "letty"})) // ["Mavis", "Senaida", "Letty"] + fmt.Println(capitalize([]string{"samuel", "MABELLE", "letitia", "meridith"})) // ["Samuel", "Mabelle", "Letitia", "Meridith"] + fmt.Println(capitalize([]string{"Slyvia", "Kristal", "Sharilyn", "Calista"})) // ["Slyvia", "Kristal", "Sharilyn", "Calista"] } diff --git a/exercise2/problem3/problem3.go b/exercise2/problem3/problem3.go index f183fafb..594cb9c5 100644 --- a/exercise2/problem3/problem3.go +++ b/exercise2/problem3/problem3.go @@ -1,5 +1,7 @@ package main +import "fmt" + type dir string const ( @@ -9,5 +11,55 @@ const ( lr dir = "lr" ) -func diagonalize() { +func diagonalize(n int, d dir) [][]int { + + matrix := make([][]int, n) + for i := range matrix { + matrix[i] = make([]int, n) + } + + switch d { + case ul: + for i := 0; i < n; i++ { + for j := 0; j < n; j++ { + matrix[i][j] = i + j + } + } + case ur: + for i := 0; i < n; i++ { + for j := 0; j < n; j++ { + matrix[i][j] = (n - 1 - j) + i + } + } + case ll: + for i := 0; i < n; i++ { + for j := 0; j < n; j++ { + matrix[i][j] = (n - 1 - i) + j + } + } + case lr: + for i := 0; i < n; i++ { + for j := 0; j < n; j++ { + matrix[i][j] = (n - 1 - i) + (n - 1 - j) + } + } + } + + return matrix +} + +func main() { + + n := 3 + result := diagonalize(n, ul) + for _, row := range result { + fmt.Println(row) + } + + fmt.Println() + + result = diagonalize(4, ur) + for _, row := range result { + fmt.Println(row) + } } diff --git a/exercise2/problem4/problem4.go b/exercise2/problem4/problem4.go index 1f680a4d..4ad163c1 100644 --- a/exercise2/problem4/problem4.go +++ b/exercise2/problem4/problem4.go @@ -1,4 +1,22 @@ package problem4 -func mapping() { +import ( + "fmt" + "strings" +) + +func mapping(letters []string) map[string]string { + result := make(map[string]string) + for _, letter := range letters { + + result[letter] = strings.ToUpper(letter) + } + return result +} + +func main() { + + fmt.Println(mapping([]string{"p", "s"})) + fmt.Println(mapping([]string{"a", "b", "c"})) + fmt.Println(mapping([]string{"a", "v", "y", "z"})) } diff --git a/exercise2/problem5/problem5.go b/exercise2/problem5/problem5.go index 43fb96a4..606c5ae7 100644 --- a/exercise2/problem5/problem5.go +++ b/exercise2/problem5/problem5.go @@ -1,4 +1,22 @@ package problem5 -func products() { +import "sort" + +func products(catalog map[string]int, minimalPrice int) []string { + var result []string + + for product, price := range catalog { + if price >= minimalPrice { + result = append(result, product) + } + } + + sort.Slice(result, func(i, j int) bool { + if catalog[result[i]] == catalog[result[j]] { + return result[i] < result[j] + } + return catalog[result[i]] > catalog[result[j]] + }) + + return result } diff --git a/exercise2/problem6/problem6.go b/exercise2/problem6/problem6.go index 89fc5bfe..dcea9da9 100644 --- a/exercise2/problem6/problem6.go +++ b/exercise2/problem6/problem6.go @@ -1,4 +1,29 @@ package problem6 -func sumOfTwo() { +import "fmt" + +func sumOfTwo(a, b []int, v int) bool { + + complements := make(map[int]bool) + + for _, numA := range a { + complements[v-numA] = true + } + + for _, numB := range b { + + if complements[numB] { + return true + } + } + + return false +} + +func main() { + + fmt.Println(sumOfTwo([]int{1, 2}, []int{4, 5, 6}, 5)) // ➞ true + fmt.Println(sumOfTwo([]int{1, 2}, []int{4, 5, 6}, 8)) // ➞ true + fmt.Println(sumOfTwo([]int{1, 2}, []int{4, 5, 6}, 3)) // ➞ false + fmt.Println(sumOfTwo([]int{1, 2}, []int{4, 5, 6}, 9)) // ➞ false } diff --git a/exercise2/problem7/problem7.go b/exercise2/problem7/problem7.go index 32514209..3b5c4e32 100644 --- a/exercise2/problem7/problem7.go +++ b/exercise2/problem7/problem7.go @@ -1,4 +1,16 @@ package problem7 -func swap() { +import "fmt" + +func swap(x, y *int) { + + temp := *x + *x = *y + *y = temp +} + +func main() { + a, b := 1, 2 + swap(&a, &b) + fmt.Println(a, b) } diff --git a/exercise2/problem8/problem8.go b/exercise2/problem8/problem8.go index 9389d3b0..db5d9273 100644 --- a/exercise2/problem8/problem8.go +++ b/exercise2/problem8/problem8.go @@ -1,16 +1,25 @@ package problem8 -func simplify(list []string) map[string]int { - var indMap map[string]int +import "fmt" - indMap = make(map[string]int) - load(&indMap, &list) +func simplify(list []string) map[string]int { + indMap := make(map[string]int) + load(indMap, list) return indMap } -func load(m *map[string]int, students *[]string) { - for i, name := range *students { - (*m)[name] = i +func load(m map[string]int, students []string) { + for i, name := range students { + m[name] = i } } + +func main() { + + result1 := simplify([]string{"a", "b", "c"}) + fmt.Println(result1) + + result2 := simplify([]string{"z", "y", "x", "u", "v"}) + fmt.Println(result2) +} diff --git a/exercise2/problem9/problem9.go b/exercise2/problem9/problem9.go index fc96d21a..ff3e446f 100644 --- a/exercise2/problem9/problem9.go +++ b/exercise2/problem9/problem9.go @@ -1,3 +1,25 @@ package problem9 -func factory() {} +import ( + "fmt" +) + +func factory(multiplier int) func(nums ...int) []int { + return func(nums ...int) []int { + + results := make([]int, len(nums)) + for i, num := range nums { + results[i] = num * multiplier + } + return results + } +} + +func main() { + + first := factory(15) + fmt.Println(first(2, 3, 4)) + + second := factory(2) + fmt.Println(second(1, 2, 3, 4)) +} From a3b333dbf709e47bf4842f8a854dc1ca747e0620 Mon Sep 17 00:00:00 2001 From: Yerkibayev Zurab Date: Sun, 6 Oct 2024 20:04:26 +0500 Subject: [PATCH 3/8] add solution for exercise3 --- exercise3/problem1/problem1.go | 34 +++++++++- exercise3/problem2/problem2.go | 36 ++++++++++- exercise3/problem3/problem3.go | 109 ++++++++++++++++++++++++++++++++- exercise3/problem4/problem4.go | 109 ++++++++++++++++++++++++++++++++- exercise3/problem5/problem5.go | 30 ++++++++- exercise3/problem6/problem6.go | 30 ++++++++- exercise3/problem7/problem7.go | 55 +++++++++++++++++ 7 files changed, 395 insertions(+), 8 deletions(-) diff --git a/exercise3/problem1/problem1.go b/exercise3/problem1/problem1.go index d45605c6..4e3be9a4 100644 --- a/exercise3/problem1/problem1.go +++ b/exercise3/problem1/problem1.go @@ -1,3 +1,35 @@ package problem1 -type Queue struct{} +import "errors" + +type Queue struct { + elements []any +} + +func (q *Queue) Enqueue(value any) { + q.elements = append(q.elements, value) +} + +func (q *Queue) Dequeue() (any, error) { + if q.IsEmpty() { + return nil, errors.New("Queue is empty") + } + val := q.elements[0] + q.elements = q.elements[1:] + return val, nil +} + +func (q *Queue) Peek() (any, error) { + if q.IsEmpty() { + return nil, errors.New("Queue is empty") + } + return q.elements[0], nil +} + +func (q *Queue) Size() int { + return len(q.elements) +} + +func (q *Queue) IsEmpty() bool { + return len(q.elements) == 0 +} diff --git a/exercise3/problem2/problem2.go b/exercise3/problem2/problem2.go index e9059889..211fcbc2 100644 --- a/exercise3/problem2/problem2.go +++ b/exercise3/problem2/problem2.go @@ -1,3 +1,37 @@ package problem2 -type Stack struct{} +import "errors" + +type Stack struct { + elements []any +} + +func (s *Stack) Push(value any) { + s.elements = append(s.elements, value) +} + +func (s *Stack) Pop() (any, error) { + if s.IsEmpty() { + return nil, errors.New("Stack is empty") + } + + index := len(s.elements) - 1 + val := s.elements[index] + s.elements = s.elements[:index] + return val, nil +} + +func (s *Stack) Peek() (any, error) { + if s.IsEmpty() { + return nil, errors.New("Stack is empty") + } + return s.elements[len(s.elements)-1], nil +} + +func (s *Stack) Size() int { + return len(s.elements) +} + +func (s *Stack) IsEmpty() bool { + return len(s.elements) == 0 +} diff --git a/exercise3/problem3/problem3.go b/exercise3/problem3/problem3.go index d8d79ac0..dfb4eb88 100644 --- a/exercise3/problem3/problem3.go +++ b/exercise3/problem3/problem3.go @@ -1,3 +1,110 @@ package problem3 -type Set struct{} +import ( + "errors" +) + +type Set struct { + elements map[any]struct{} +} + +func NewSet() *Set { + return &Set{ + elements: make(map[any]struct{}), + } +} + +func (s *Set) Add(value any) { + s.elements[value] = struct{}{} +} + +func (s *Set) Remove(value any) { + delete(s.elements, value) +} + +func (s *Set) IsEmpty() bool { + return len(s.elements) == 0 +} + +func (s *Set) Size() int { + return len(s.elements) +} + +func (s *Set) List() []any { + list := make([]any, 0, len(s.elements)) + for key := range s.elements { + list = append(list, key) + } + return list +} + +func (s *Set) Has(value any) bool { + _, exists := s.elements[value] + return exists +} + +func (s *Set) Copy() *Set { + newSet := NewSet() + for key := range s.elements { + newSet.Add(key) + } + return newSet +} + +func (s *Set) Difference(other *Set) *Set { + diffSet := NewSet() + for key := range s.elements { + if !other.Has(key) { + diffSet.Add(key) + } + } + return diffSet +} + +func (s *Set) IsSubset(other *Set) bool { + for key := range s.elements { + if !other.Has(key) { + return false + } + } + return true +} + +func Union(sets ...*Set) *Set { + unionSet := NewSet() + for _, set := range sets { + for key := range set.elements { + unionSet.Add(key) + } + } + return unionSet +} + +func Intersect(sets ...*Set) *Set { + if len(sets) == 0 { + return NewSet() + } + + intersectSet := sets[0].Copy() + for _, set := range sets[1:] { + for key := range intersectSet.elements { + if !set.Has(key) { + intersectSet.Remove(key) + } + } + } + return intersectSet +} + +func (s *Set) Pop() (any, error) { + if s.IsEmpty() { + return nil, errors.New("Set is empty") + } + var key any + for k := range s.elements { + key = k + break + } + s.Remove(key) + return key, nil +} diff --git a/exercise3/problem4/problem4.go b/exercise3/problem4/problem4.go index ebf78147..a284f700 100644 --- a/exercise3/problem4/problem4.go +++ b/exercise3/problem4/problem4.go @@ -1,3 +1,110 @@ package problem4 -type LinkedList struct{} +import ( + "errors" + "fmt" +) + +type Element[T comparable] struct { + value T + next *Element[T] +} + +type LinkedList[T comparable] struct { + head *Element[T] +} + +func (list *LinkedList[T]) Add(element *Element[T]) { + newElement := element + + if list.head == nil { + list.head = newElement + } else { + current := list.head + for current.next != nil { + current = current.next + } + current.next = newElement + } +} + +func (list *LinkedList[T]) Insert(element *Element[T], index int) error { + if index < 0 || index > list.Size() { + return errors.New("index out of range") + } + + i := 1 + if index == i { + temp := list.head + list.head = element + list.head.next = temp + return nil + } + + current := list.head + for current.next != nil { + i++ + fmt.Println(current.value) + if i == index { + temp := current.next + current.next = element + current.next.next = temp + return nil + } + current = current.next + } + return nil +} + +func (list *LinkedList[T]) Delete(element *Element[T]) error { + if list.head.value == element.value { + list.head = list.head.next + } + current := list.head + if current == nil { + return errors.New("list is empty") + } + for current.next != nil { + if current.next.value == element.value { + current.next = current.next.next + } + current = current.next + } + return errors.New("element to delete not found") +} + +func (list *LinkedList[T]) Find(value any) (Element[T], error) { + if list.head.value == value { + return *list.head, nil + } + current := list.head + for current.next != nil { + if current.next.value == value { + return *current.next, nil + } + current = current.next + } + return Element[T]{}, errors.New("element not found") +} + +func (list *LinkedList[T]) List() []T { + var result []T + if list.head == nil { + return result + } + current := list.head + result = append(result, current.value) + for current.next != nil { + current = current.next + result = append(result, current.value) + } + return result +} + +func (list *LinkedList[T]) Size() int { + return len(list.List()) +} + +func (list *LinkedList[T]) IsEmpty() bool { + return list.Size() == 0 +} diff --git a/exercise3/problem5/problem5.go b/exercise3/problem5/problem5.go index 4177599f..3e8f2e2c 100644 --- a/exercise3/problem5/problem5.go +++ b/exercise3/problem5/problem5.go @@ -1,3 +1,31 @@ package problem5 -type Person struct{} +import ( + "fmt" +) + +type Person struct { + Name string + Age int +} + +func (p *Person) compareAge(other *Person) string { + if p.Age > other.Age { + return fmt.Sprintf("%s is younger than me.", other.Name) + } else if p.Age < other.Age { + return fmt.Sprintf("%s is older than me.", other.Name) + } else { + return fmt.Sprintf("%s is the same age as me.", other.Name) + } +} + +func main() { + + p1 := &Person{"Samuel", 24} + p2 := &Person{"Joel", 36} + p3 := &Person{"Lily", 24} + + fmt.Println(p1.compareAge(p2)) + fmt.Println(p2.compareAge(p1)) + fmt.Println(p1.compareAge(p3)) +} diff --git a/exercise3/problem6/problem6.go b/exercise3/problem6/problem6.go index 4e8d1af8..5c10f795 100644 --- a/exercise3/problem6/problem6.go +++ b/exercise3/problem6/problem6.go @@ -1,7 +1,31 @@ package problem6 -type Animal struct{} +type Legged interface { + GetLegsNum() int +} -type Insect struct{} +type Animal struct { + name string + legsNum int +} -func sumOfAllLegsNum() {} +func (a *Animal) GetLegsNum() int { + return a.legsNum +} + +type Insect struct { + name string + legsNum int +} + +func (i *Insect) GetLegsNum() int { + return i.legsNum +} + +func sumOfAllLegsNum(leggeds ...Legged) int { + totalLegs := 0 + for _, legged := range leggeds { + totalLegs += legged.GetLegsNum() + } + return totalLegs +} diff --git a/exercise3/problem7/problem7.go b/exercise3/problem7/problem7.go index 26887151..a77a4112 100644 --- a/exercise3/problem7/problem7.go +++ b/exercise3/problem7/problem7.go @@ -1,10 +1,65 @@ package problem7 +import "fmt" + type BankAccount struct { + name string + balance int +} + +func (account *BankAccount) setBalance(amount int) { + account.balance = account.balance - amount } type FedexAccount struct { + name string + packages []string +} + +func (account *FedexAccount) getName() string { + return account.name +} + +func (account *FedexAccount) setPackage(pkg string) { + account.packages = append(account.packages, pkg) } type KazPostAccount struct { + name string + balance int + packages []string +} + +func (account *KazPostAccount) getName() string { + return account.name +} + +func (account *KazPostAccount) setBalance(amount int) { + account.balance = account.balance - amount +} + +func (account *KazPostAccount) setPackage(pkg string) { + account.packages = append(account.packages, pkg) +} + +type MinusMoney interface { + setBalance(balance int) +} + +func withdrawMoney(money int, acc ...MinusMoney) { + for _, m := range acc { + m.setBalance(money) + } +} + +type SendSmth interface { + getName() string + setPackage(pkg string) +} + +func sendPackagesTo(whom string, acc ...SendSmth) { + for _, m := range acc { + toWhom := fmt.Sprintf("%s send package to %s", m.getName(), whom) + m.setPackage(toWhom) + } } From 1825418bcf6ba7fed4ad3bd3e59d26d28d86a922 Mon Sep 17 00:00:00 2001 From: Yerkibayev Zurab Date: Sun, 3 Nov 2024 23:34:15 +0500 Subject: [PATCH 4/8] add solution for exercise5 --- exercise5/problem1/problem1.go | 7 ++++++ exercise5/problem2/problem2.go | 39 ++++++++++++++++++++++++++++++++++ exercise5/problem3/problem3.go | 5 ++++- exercise5/problem4/problem4.go | 1 + exercise5/problem5/problem5.go | 19 +++++++++++++++-- exercise5/problem6/problem6.go | 29 ++++++++++++++++++++++--- exercise5/problem7/problem7.go | 32 +++++++++++++++++++++++++++- exercise5/problem8/problem8.go | 9 +++++++- 8 files changed, 133 insertions(+), 8 deletions(-) diff --git a/exercise5/problem1/problem1.go b/exercise5/problem1/problem1.go index 4f514fab..8e71df1a 100644 --- a/exercise5/problem1/problem1.go +++ b/exercise5/problem1/problem1.go @@ -1,9 +1,16 @@ package problem1 +import "sync" + func incrementConcurrently(num int) int { + wg := new(sync.WaitGroup) + wg.Add(1) + go func() { + defer wg.Done() num++ }() + wg.Wait() return num } diff --git a/exercise5/problem2/problem2.go b/exercise5/problem2/problem2.go index 16d38e1d..b568b2fc 100644 --- a/exercise5/problem2/problem2.go +++ b/exercise5/problem2/problem2.go @@ -1,5 +1,10 @@ package problem2 +import ( + "runtime" + "sync" +) + // add - sequential code to add numbers, don't update it, just to illustrate concept func add(numbers []int) int64 { var sum int64 @@ -10,7 +15,41 @@ func add(numbers []int) int64 { } func addConcurrently(numbers []int) int64 { + cpuCount := runtime.NumCPU() + runtime.GOMAXPROCS(cpuCount) + chunkSize := len(numbers) / cpuCount + resultCh := make(chan int, cpuCount) + wg := new(sync.WaitGroup) + var sum int64 + for i := 0; i < cpuCount; i++ { + start := i * chunkSize + end := start + chunkSize + if i == cpuCount-1 { + end = len(numbers) + } + wg.Add(1) + go processAddChunks(numbers[start:end], resultCh, wg) + } + + go func() { + wg.Wait() + close(resultCh) + }() + + for result := range resultCh { + sum += int64(result) + } + return sum } + +func processAddChunks(chunk []int, resultCh chan<- int, wg *sync.WaitGroup) { + defer wg.Done() + result := 0 + for _, num := range chunk { + result += num + } + resultCh <- result +} diff --git a/exercise5/problem3/problem3.go b/exercise5/problem3/problem3.go index e085a51a..416190eb 100644 --- a/exercise5/problem3/problem3.go +++ b/exercise5/problem3/problem3.go @@ -2,10 +2,13 @@ package problem3 func sum(a, b int) int { var c int + ch := make(chan int) // создаем канал для передачи результата go func(a, b int) { - c = a + b + ch <- a + b // отправляем результат в канал }(a, b) + c = <-ch // получаем результат из канала и присваиваем его переменной c + return c } diff --git a/exercise5/problem4/problem4.go b/exercise5/problem4/problem4.go index b5899ddf..6c05ac2e 100644 --- a/exercise5/problem4/problem4.go +++ b/exercise5/problem4/problem4.go @@ -4,6 +4,7 @@ func iter(ch chan<- int, nums []int) { for _, n := range nums { ch <- n } + close(ch) } func sum(nums []int) int { diff --git a/exercise5/problem5/problem5.go b/exercise5/problem5/problem5.go index ac192c58..df15a156 100644 --- a/exercise5/problem5/problem5.go +++ b/exercise5/problem5/problem5.go @@ -1,8 +1,23 @@ package problem5 -func producer() {} +import ( + "strings" +) -func consumer() {} +func producer(words []string, ch chan<- string) { + for _, word := range words { + ch <- word // Отправляем каждое слово в канал + } + close(ch) // Закрываем канал после отправки всех слов +} + +func consumer(ch <-chan string) string { + var result []string + for word := range ch { + result = append(result, word) // Добавляем каждое слово в срез + } + return strings.Join(result, " ") // Объединяем слова в одну строку с пробелами +} func send( words []string, diff --git a/exercise5/problem6/problem6.go b/exercise5/problem6/problem6.go index e1beea87..b54eda65 100644 --- a/exercise5/problem6/problem6.go +++ b/exercise5/problem6/problem6.go @@ -2,8 +2,31 @@ package problem6 type pipe func(in <-chan int) <-chan int -var multiplyBy2 pipe = func() {} +var multiplyBy2 pipe = func(in <-chan int) <-chan int { + out := make(chan int) + go func() { + defer close(out) + for n := range in { + out <- n * 2 + } + }() + return out +} -var add5 pipe = func() {} +var add5 pipe = func(in <-chan int) <-chan int { + out := make(chan int) + go func() { + defer close(out) + for n := range in { + out <- n + 5 + } + }() + return out +} -func piper(in <-chan int, pipes []pipe) <-chan int {} +func piper(in <-chan int, pipes []pipe) <-chan int { + for _, p := range pipes { + in = p(in) + } + return in +} diff --git a/exercise5/problem7/problem7.go b/exercise5/problem7/problem7.go index c3c1d0c9..3f63cdb6 100644 --- a/exercise5/problem7/problem7.go +++ b/exercise5/problem7/problem7.go @@ -1,3 +1,33 @@ package problem7 -func multiplex(ch1 <-chan string, ch2 <-chan string) []string {} +func multiplex(ch1 <-chan string, ch2 <-chan string) []string { + var results []string + out := make(chan string) + done := make(chan struct{}) + + go func() { + for val := range ch1 { + out <- val + } + done <- struct{}{} + }() + + go func() { + for val := range ch2 { + out <- val + } + done <- struct{}{} + }() + + go func() { + <-done + <-done + close(out) + }() + + for val := range out { + results = append(results, val) + } + + return results +} diff --git a/exercise5/problem8/problem8.go b/exercise5/problem8/problem8.go index 3e951b3b..cde2d620 100644 --- a/exercise5/problem8/problem8.go +++ b/exercise5/problem8/problem8.go @@ -4,4 +4,11 @@ import ( "time" ) -func withTimeout(ch <-chan string, ttl time.Duration) string {} +func withTimeout(ch <-chan string, ttl time.Duration) string { + select { + case msg := <-ch: + return msg + case <-time.After(ttl): + return "timeout" + } +} From df56a3399c6313c52b80e069e60456edb5a1910d Mon Sep 17 00:00:00 2001 From: Yerkibayev Zurab Date: Sun, 17 Nov 2024 22:39:05 +0500 Subject: [PATCH 5/8] add solution for exercise6 --- exercise6/problem1/problem1.go | 23 +++++++++++++++++++++-- exercise6/problem2/problem2.go | 24 +++++++++++++++++++++--- exercise6/problem3/problem3.go | 16 ++++++++++++++++ exercise6/problem4/problem4.go | 25 ++++++++++++++++++------- exercise6/problem5/problem5.go | 23 +++++++++++++++++------ exercise6/problem6/problem6.go | 5 +++-- exercise6/problem7/problem7.go | 5 +++++ exercise6/problem8/problem8.go | 28 +++++++++++++++++++++++++++- 8 files changed, 128 insertions(+), 21 deletions(-) diff --git a/exercise6/problem1/problem1.go b/exercise6/problem1/problem1.go index ee453b24..d600d144 100644 --- a/exercise6/problem1/problem1.go +++ b/exercise6/problem1/problem1.go @@ -1,9 +1,28 @@ package problem1 +import "sync" + type bankAccount struct { - blnc int + blnc int + mutex sync.Mutex } func newAccount(blnc int) *bankAccount { - return &bankAccount{blnc} + return &bankAccount{blnc: blnc} +} + +func (a *bankAccount) deposit(amount int) { + a.mutex.Lock() + defer a.mutex.Unlock() + + a.blnc += amount +} + +func (a *bankAccount) withdraw(amount int) { + a.mutex.Lock() + defer a.mutex.Unlock() + + if a.blnc >= amount { + a.blnc -= amount + } } diff --git a/exercise6/problem2/problem2.go b/exercise6/problem2/problem2.go index 97e02368..9c57c36a 100644 --- a/exercise6/problem2/problem2.go +++ b/exercise6/problem2/problem2.go @@ -1,20 +1,38 @@ package problem2 import ( + "sync" "time" ) var readDelay = 10 * time.Millisecond type bankAccount struct { - blnc int + blnc int + mutex sync.Mutex } func newAccount(blnc int) *bankAccount { - return &bankAccount{blnc} + return &bankAccount{blnc: blnc} +} + +func (b *bankAccount) deposit(amount int) { + b.mutex.Lock() + defer b.mutex.Unlock() + b.blnc += amount +} + +func (b *bankAccount) withdraw(amount int) { + b.mutex.Lock() + defer b.mutex.Unlock() + if b.blnc >= amount { + b.blnc -= amount + } } func (b *bankAccount) balance() int { time.Sleep(readDelay) - return 0 + b.mutex.Lock() + defer b.mutex.Unlock() + return b.blnc } diff --git a/exercise6/problem3/problem3.go b/exercise6/problem3/problem3.go index b34b90bb..355679c9 100644 --- a/exercise6/problem3/problem3.go +++ b/exercise6/problem3/problem3.go @@ -1,5 +1,9 @@ package problem3 +import ( + "sync/atomic" +) + type counter struct { val int64 } @@ -9,3 +13,15 @@ func newCounter() *counter { val: 0, } } + +func (c *counter) inc() { + atomic.AddInt64(&c.val, 1) +} + +func (c *counter) dec() { + atomic.AddInt64(&c.val, -1) +} + +func (c *counter) value() int64 { + return atomic.LoadInt64(&c.val) +} diff --git a/exercise6/problem4/problem4.go b/exercise6/problem4/problem4.go index 793449c9..feb89eed 100644 --- a/exercise6/problem4/problem4.go +++ b/exercise6/problem4/problem4.go @@ -1,28 +1,39 @@ package problem4 import ( + "sync" "time" ) -func worker(id int, _ *[]string, ch chan<- int) { - // TODO wait for shopping list to be completed +var listCond = sync.NewCond(&sync.Mutex{}) +var listFilled bool + +func worker(id int, shoppingList *[]string, ch chan<- int) { + listCond.L.Lock() + for !listFilled { + listCond.Wait() + } ch <- id + listCond.L.Unlock() } func updateShopList(shoppingList *[]string) { time.Sleep(10 * time.Millisecond) - *shoppingList = append(*shoppingList, "apples") - *shoppingList = append(*shoppingList, "milk") - *shoppingList = append(*shoppingList, "bake soda") + *shoppingList = append(*shoppingList, "apples", "milk", "bake soda") + + listCond.L.Lock() + listFilled = true + listCond.Signal() + listCond.L.Unlock() } func notifyOnShopListUpdate(shoppingList *[]string, numWorkers int) <-chan int { notifier := make(chan int) - for i := range numWorkers { + for i := 0; i < numWorkers; i++ { go worker(i+1, shoppingList, notifier) - time.Sleep(time.Millisecond) // order matters + time.Sleep(time.Millisecond) } go updateShopList(shoppingList) diff --git a/exercise6/problem5/problem5.go b/exercise6/problem5/problem5.go index 8e4a1703..21ec233f 100644 --- a/exercise6/problem5/problem5.go +++ b/exercise6/problem5/problem5.go @@ -1,28 +1,39 @@ package problem5 import ( + "sync" "time" ) +var listCond = sync.NewCond(&sync.Mutex{}) +var listFilled bool + func worker(id int, shoppingList *[]string, ch chan<- int) { - // TODO wait for shopping list to be completed + listCond.L.Lock() + for !listFilled { + listCond.Wait() + } ch <- id + listCond.L.Unlock() } func updateShopList(shoppingList *[]string) { time.Sleep(10 * time.Millisecond) - *shoppingList = append(*shoppingList, "apples") - *shoppingList = append(*shoppingList, "milk") - *shoppingList = append(*shoppingList, "bake soda") + *shoppingList = append(*shoppingList, "apples", "milk", "bake soda") + + listCond.L.Lock() + listFilled = true + listCond.Broadcast() + listCond.L.Unlock() } func notifyOnShopListUpdate(shoppingList *[]string, numWorkers int) <-chan int { notifier := make(chan int) - for i := range numWorkers { + for i := 0; i < numWorkers; i++ { go worker(i+1, shoppingList, notifier) - time.Sleep(time.Millisecond) // order matters + time.Sleep(time.Millisecond) } go updateShopList(shoppingList) diff --git a/exercise6/problem6/problem6.go b/exercise6/problem6/problem6.go index 0c1122b9..27e00196 100644 --- a/exercise6/problem6/problem6.go +++ b/exercise6/problem6/problem6.go @@ -4,6 +4,8 @@ import ( "sync" ) +var once sync.Once + func runTasks(init func()) { var wg sync.WaitGroup @@ -12,8 +14,7 @@ func runTasks(init func()) { go func() { defer wg.Done() - //TODO: modify so that load function gets called only once. - init() + once.Do(init) }() } wg.Wait() diff --git a/exercise6/problem7/problem7.go b/exercise6/problem7/problem7.go index ef49497b..4fb0767e 100644 --- a/exercise6/problem7/problem7.go +++ b/exercise6/problem7/problem7.go @@ -3,15 +3,20 @@ package problem7 import ( "fmt" "math/rand" + "sync" "time" ) func task() { start := time.Now() var t *time.Timer + var mu sync.Mutex + t = time.AfterFunc( randomDuration(), func() { + mu.Lock() + defer mu.Unlock() fmt.Println(time.Now().Sub(start)) t.Reset(randomDuration()) }, diff --git a/exercise6/problem8/problem8.go b/exercise6/problem8/problem8.go index 949eb2d2..2a6f55d8 100644 --- a/exercise6/problem8/problem8.go +++ b/exercise6/problem8/problem8.go @@ -1,3 +1,29 @@ package problem8 -func multiplex(chs []<-chan string) []string {} +func multiplex(chs []<-chan string) []string { + resultChan := make(chan string) + results := []string{} + done := make(chan struct{}) + + for _, ch := range chs { + go func(ch <-chan string) { + for val := range ch { + resultChan <- val + } + done <- struct{}{} + }(ch) + } + + go func() { + for range chs { + <-done + } + close(resultChan) + }() + + for val := range resultChan { + results = append(results, val) + } + + return results +} From 0775bd44d12c69eadba90f0aef61ccd0a3bbb121 Mon Sep 17 00:00:00 2001 From: Talgat Date: Thu, 23 Jan 2025 19:03:48 +0500 Subject: [PATCH 6/8] add exercise9 --- exercise9/README.md | 42 ++++++++++++++++++++++++++++++++++++++++++ exercise9/go.mod | 3 +++ 2 files changed, 45 insertions(+) create mode 100644 exercise9/README.md create mode 100644 exercise9/go.mod diff --git a/exercise9/README.md b/exercise9/README.md new file mode 100644 index 00000000..5d54880e --- /dev/null +++ b/exercise9/README.md @@ -0,0 +1,42 @@ +# Exercise 9 + +Project + +## Teams + +Team 1 + +1. Имангали Аскар (controller) +2. Зернов Владислав (controller) +3. Курмашев Сабит (api) +4. Кабулов Нуртас (api) +5. Омаров Темирлан (db) +6. Сагиндиков Меирбек (db) + +Team 2 + +1. Тұрарова Айзада (controller) +2. Толеу Аян (controller) +3. Мырзаханов Алинур (api) +4. Еркибаев Зураб (api) +5. Бақатай Ақжол (db) +6. Бимаканова Мадина (db) + +Team 3 + +1. Кабдылкак Арнур (controller) +2. Калкин Ернар (controller) +3. Манкенов Арай (api) +4. Усербай Асылбек (api) +5. Камбаров Руслан (db) +6. Қайратұлы Шыңғысхан (db) + +Team 4 + +1. Жақуда Жарқынай (controller) +2. Жантасов Адлет (controller) +3. Туралин Аргын (api) +4. Алтынбек Жандос (api) +5. Жакупов Жандаулет (db) +6. Мұхаметқали Арайлым (db) +7. Кемалатдин Ғалымжан (your choice) diff --git a/exercise9/go.mod b/exercise9/go.mod new file mode 100644 index 00000000..72f28b6f --- /dev/null +++ b/exercise9/go.mod @@ -0,0 +1,3 @@ +module github.com/talgat-ruby/exercises-go/exercise9 + +go 1.23.5 From 753fb05717e34fa7f5ab8fe21338794728a2c75c Mon Sep 17 00:00:00 2001 From: Talgat Date: Mon, 27 Jan 2025 18:18:10 +0500 Subject: [PATCH 7/8] update exercise9 --- exercise9/README.md | 40 ++++++---------------------------------- 1 file changed, 6 insertions(+), 34 deletions(-) diff --git a/exercise9/README.md b/exercise9/README.md index 5d54880e..ef1d468e 100644 --- a/exercise9/README.md +++ b/exercise9/README.md @@ -6,37 +6,9 @@ Project Team 1 -1. Имангали Аскар (controller) -2. Зернов Владислав (controller) -3. Курмашев Сабит (api) -4. Кабулов Нуртас (api) -5. Омаров Темирлан (db) -6. Сагиндиков Меирбек (db) - -Team 2 - -1. Тұрарова Айзада (controller) -2. Толеу Аян (controller) -3. Мырзаханов Алинур (api) -4. Еркибаев Зураб (api) -5. Бақатай Ақжол (db) -6. Бимаканова Мадина (db) - -Team 3 - -1. Кабдылкак Арнур (controller) -2. Калкин Ернар (controller) -3. Манкенов Арай (api) -4. Усербай Асылбек (api) -5. Камбаров Руслан (db) -6. Қайратұлы Шыңғысхан (db) - -Team 4 - -1. Жақуда Жарқынай (controller) -2. Жантасов Адлет (controller) -3. Туралин Аргын (api) -4. Алтынбек Жандос (api) -5. Жакупов Жандаулет (db) -6. Мұхаметқали Арайлым (db) -7. Кемалатдин Ғалымжан (your choice) +1. Тұрарова Айзада (api) +2. Манкенов Арай (api) +3. Усербай Асылбек (controller) +4. Кемалатдин Ғалымжан (controller) +5. Имангали Аскар (db) +6. Кабдылкак Арнур (db) From ddc781d6efd71e2ee79cef0dccbd7f3e08bce907 Mon Sep 17 00:00:00 2001 From: Yerkibayev Zurab Date: Fri, 28 Feb 2025 23:01:55 +0500 Subject: [PATCH 8/8] BLOGGING-PLATFORM --- exercise7/blogging-platform/.dockerignore | 25 +++ exercise7/blogging-platform/Dockerfile | 30 +++ .../blogging-platform/docker-compose.yml | 30 +++ exercise7/blogging-platform/go.mod | 9 +- .../blogging-platform/internal/api/api.go | 192 ++++++++++++++++++ exercise7/blogging-platform/internal/db/db.go | 54 +++++ exercise7/blogging-platform/main.go | 38 ++-- .../pkg/httputils/request/body.go | 2 +- 8 files changed, 353 insertions(+), 27 deletions(-) create mode 100644 exercise7/blogging-platform/.dockerignore create mode 100644 exercise7/blogging-platform/Dockerfile create mode 100644 exercise7/blogging-platform/docker-compose.yml create mode 100644 exercise7/blogging-platform/internal/api/api.go create mode 100644 exercise7/blogging-platform/internal/db/db.go diff --git a/exercise7/blogging-platform/.dockerignore b/exercise7/blogging-platform/.dockerignore new file mode 100644 index 00000000..3dbbcf3e --- /dev/null +++ b/exercise7/blogging-platform/.dockerignore @@ -0,0 +1,25 @@ +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/bin +**/charts +**/docker-compose* +**/compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md diff --git a/exercise7/blogging-platform/Dockerfile b/exercise7/blogging-platform/Dockerfile new file mode 100644 index 00000000..86af6d54 --- /dev/null +++ b/exercise7/blogging-platform/Dockerfile @@ -0,0 +1,30 @@ +# Используем официальный образ Go +FROM golang:1.23 AS builder + +# Устанавливаем рабочую директорию внутри контейнера +WORKDIR /app + +# Копируем файлы модуля и устанавливаем зависимости +COPY go.mod go.sum ./ +RUN go mod tidy + +# Копируем исходный код +COPY . . + +# Собираем Go-приложение +RUN go build -o app + +# Новый контейнер для финального образа +FROM golang:1.23 + +# Устанавливаем рабочую директорию +WORKDIR /app + +# Копируем скомпилированное приложение +COPY --from=builder /app/app . + +# Открываем порт +EXPOSE 8080 + +# Запускаем сервер +CMD ["./app"] diff --git a/exercise7/blogging-platform/docker-compose.yml b/exercise7/blogging-platform/docker-compose.yml new file mode 100644 index 00000000..43e784ac --- /dev/null +++ b/exercise7/blogging-platform/docker-compose.yml @@ -0,0 +1,30 @@ +version: "3.8" + +services: + db: + image: postgres:15 + container_name: blogging-platform-db + restart: always + environment: + POSTGRES_USER: user + POSTGRES_PASSWORD: password + POSTGRES_DB: blogging_platform + ports: + - "5432:5432" + volumes: + - postgres_data:/var/lib/postgresql/data + + app: + build: . + container_name: blogging-platform-app + restart: always + depends_on: + - db + environment: + - DB_URL=postgres://user:password@db:5432/blogging_platform?sslmode=disable + ports: + - "8080:8080" + command: [ "./app" ] # Команда для запуска контейнера + +volumes: + postgres_data: diff --git a/exercise7/blogging-platform/go.mod b/exercise7/blogging-platform/go.mod index ca16e703..86d0ba0e 100644 --- a/exercise7/blogging-platform/go.mod +++ b/exercise7/blogging-platform/go.mod @@ -1,5 +1,10 @@ -module github.com/talgat-ruby/exercises-go/exercise7/blogging-platform +module blogging-platform go 1.23.3 -require github.com/lib/pq v1.10.9 +require ( + github.com/YerkibayevZurab/exercises-go/exercise7/blogging-platform v0.0.0-00010101000000-000000000000 + github.com/lib/pq v1.10.9 +) + +replace github.com/YerkibayevZurab/exercises-go/exercise7/blogging-platform => . diff --git a/exercise7/blogging-platform/internal/api/api.go b/exercise7/blogging-platform/internal/api/api.go new file mode 100644 index 00000000..e6b77fe1 --- /dev/null +++ b/exercise7/blogging-platform/internal/api/api.go @@ -0,0 +1,192 @@ +package api + +import ( + "blogging-platform/internal/db" + "context" + "encoding/json" + "fmt" + "log" + "net/http" + "strconv" + "strings" + + "github.com/lib/pq" + _ "github.com/lib/pq" +) + +// API - структура для работы с API +type API struct { + server *http.Server +} + +// BlogPost - структура поста в блоге +type BlogPost struct { + ID int `json:"id"` + Title string `json:"title"` + Content string `json:"content"` + Category string `json:"category"` + Tags []string `json:"tags"` +} + +// New создаёт новый API +func New() *API { + mux := http.NewServeMux() + + // Проверка статуса сервера + mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "Server is running!") + }) + + // Получение всех постов с фильтрацией + mux.HandleFunc("/posts", func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodGet { + term := r.URL.Query().Get("term") + posts := getAllPosts(term) + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(posts) + return + } + + // Создание поста + if r.Method == http.MethodPost { + var post BlogPost + if err := json.NewDecoder(r.Body).Decode(&post); err != nil { + http.Error(w, "Invalid request body", http.StatusBadRequest) + return + } + + // Валидация данных + if post.Title == "" || post.Content == "" || post.Category == "" { + http.Error(w, "Title, Content и Category не могут быть пустыми", http.StatusBadRequest) + return + } + + id := createPost(post) + post.ID = id + + w.WriteHeader(http.StatusCreated) + json.NewEncoder(w).Encode(post) + return + } + + http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) + }) + + // Обновление и удаление постов + mux.HandleFunc("/posts/", func(w http.ResponseWriter, r *http.Request) { + idStr := strings.TrimPrefix(r.URL.Path, "/posts/") + id, err := strconv.Atoi(idStr) + if err != nil { + http.Error(w, "Invalid post ID", http.StatusBadRequest) + return + } + + if r.Method == http.MethodPut { + var updatedPost BlogPost + if err := json.NewDecoder(r.Body).Decode(&updatedPost); err != nil { + http.Error(w, "Invalid request body", http.StatusBadRequest) + return + } + + if updatedPost.Title == "" || updatedPost.Content == "" || updatedPost.Category == "" { + http.Error(w, "Title, Content и Category не могут быть пустыми", http.StatusBadRequest) + return + } + + updatePost(id, updatedPost) + updatedPost.ID = id + json.NewEncoder(w).Encode(updatedPost) + return + } + + if r.Method == http.MethodDelete { + deletePost(id) + w.WriteHeader(http.StatusNoContent) + return + } + + http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) + }) + + return &API{ + server: &http.Server{ + Addr: ":8081", + Handler: mux, + }, + } +} + +// Start запускает сервер +func (a *API) Start(ctx context.Context) error { + fmt.Println("🚀 Starting server on :8080...") + go func() { + if err := a.server.ListenAndServe(); err != http.ErrServerClosed { + fmt.Println("❌ Server error:", err) + } + }() + + <-ctx.Done() + fmt.Println("⚠️ Shutting down server...") + return a.server.Shutdown(context.Background()) +} + +// Функции для работы с БД + +// getAllPosts получает все посты с фильтрацией +func getAllPosts(term string) []BlogPost { + query := "SELECT id, title, content, category, tags FROM posts" + var args []interface{} + + if term != "" { + query += " WHERE LOWER(title) LIKE LOWER($1) OR LOWER(content) LIKE LOWER($1) OR LOWER(category) LIKE LOWER($1)" + args = append(args, "%"+term+"%") + } + + rows, err := db.DB.Query(query, args...) + if err != nil { + log.Fatalf("❌ Ошибка запроса постов: %v", err) + } + defer rows.Close() + + var posts []BlogPost + for rows.Next() { + var post BlogPost + var tags []string + if err := rows.Scan(&post.ID, &post.Title, &post.Content, &post.Category, pq.Array(&tags)); err != nil { + log.Fatalf("❌ Ошибка чтения поста: %v", err) + } + post.Tags = tags + posts = append(posts, post) + } + return posts +} + +// createPost создаёт новый пост +func createPost(post BlogPost) int { + query := "INSERT INTO posts (title, content, category, tags) VALUES ($1, $2, $3, $4) RETURNING id" + var id int + err := db.DB.QueryRow(query, post.Title, post.Content, post.Category, pq.Array(post.Tags)).Scan(&id) + if err != nil { + log.Fatalf("❌ Ошибка вставки поста: %v", err) + } + return id +} + +// updatePost обновляет существующий пост +func updatePost(id int, post BlogPost) { + query := "UPDATE posts SET title=$1, content=$2, category=$3, tags=$4 WHERE id=$5" + _, err := db.DB.Exec(query, post.Title, post.Content, post.Category, pq.Array(post.Tags), id) + if err != nil { + log.Fatalf("❌ Ошибка обновления поста: %v", err) + } +} + +// deletePost удаляет пост +func deletePost(id int) { + query := "DELETE FROM posts WHERE id=$1" + _, err := db.DB.Exec(query, id) + if err != nil { + log.Fatalf("❌ Ошибка удаления поста: %v", err) + } +} diff --git a/exercise7/blogging-platform/internal/db/db.go b/exercise7/blogging-platform/internal/db/db.go new file mode 100644 index 00000000..3cc9a2ec --- /dev/null +++ b/exercise7/blogging-platform/internal/db/db.go @@ -0,0 +1,54 @@ +package db + +import ( + "database/sql" + "fmt" + "log" + "os" + + _ "github.com/lib/pq" +) + +var DB *sql.DB + +// InitDB инициализирует подключение к базе данных +func InitDB() (*sql.DB, error) { + dbURL := os.Getenv("DB_URL") // Берём URL из переменной окружения + if dbURL == "" { + log.Fatal("❌ Ошибка: переменная окружения DB_URL не задана") + } + + fmt.Println("📡 Подключение к БД...") + database, err := sql.Open("postgres", dbURL) + if err != nil { + log.Fatalf("❌ Ошибка подключения к БД: %v", err) + } + + // Проверяем соединение + err = database.Ping() + if err != nil { + log.Fatalf("❌ Ошибка при пинге БД: %v", err) + } + + fmt.Println("✅ Подключение к PostgreSQL успешно!") + + // Создаём таблицу, если её нет + createTableQuery := ` + CREATE TABLE IF NOT EXISTS posts ( + id SERIAL PRIMARY KEY, + title TEXT NOT NULL, + content TEXT NOT NULL, + category TEXT NOT NULL, + tags TEXT[] + ); + ` + _, err = database.Exec(createTableQuery) + if err != nil { + log.Fatalf("❌ Ошибка создания таблицы: %v", err) + } + + fmt.Println("✅ Таблица 'posts' проверена или создана!") + + DB = database + return DB, nil +} diff --git a/exercise7/blogging-platform/main.go b/exercise7/blogging-platform/main.go index 1ffa1477..256d1d82 100644 --- a/exercise7/blogging-platform/main.go +++ b/exercise7/blogging-platform/main.go @@ -2,48 +2,38 @@ package main import ( "context" - "log/slog" + "fmt" + "log" "os" "os/signal" - "github.com/talgat-ruby/exercises-go/exercise7/blogging-platform/internal/api" - "github.com/talgat-ruby/exercises-go/exercise7/blogging-platform/internal/db" + "blogging-platform/internal/api" + "blogging-platform/internal/db" ) func main() { ctx, cancel := context.WithCancel(context.Background()) + defer cancel() - // db - _, err := db.New() + // Подключение к БД + _, err := db.InitDB() // Теперь вызываем `InitDB()` if err != nil { - slog.ErrorContext( - ctx, - "initialize service error", - "service", "db", - "error", err, - ) - panic(err) + log.Fatalf("❌ Ошибка подключения к БД: %v", err) } - // api + // Запуск API a := api.New() if err := a.Start(ctx); err != nil { - slog.ErrorContext( - ctx, - "initialize service error", - "service", "api", - "error", err, - ) - panic(err) + log.Fatalf("❌ Ошибка запуска API: %v", err) } + // Завершаем работу при нажатии Ctrl+C go func() { - shutdown := make(chan os.Signal, 1) // Create channel to signify s signal being sent - signal.Notify(shutdown, os.Interrupt) // When an interrupt is sent, notify the channel + shutdown := make(chan os.Signal, 1) + signal.Notify(shutdown, os.Interrupt) sig := <-shutdown - slog.WarnContext(ctx, "signal received - shutting down...", "signal", sig) - + fmt.Println("⚠️ Получен сигнал завершения:", sig) cancel() }() } diff --git a/exercise7/blogging-platform/pkg/httputils/request/body.go b/exercise7/blogging-platform/pkg/httputils/request/body.go index 92d639f4..4ff17ac8 100644 --- a/exercise7/blogging-platform/pkg/httputils/request/body.go +++ b/exercise7/blogging-platform/pkg/httputils/request/body.go @@ -8,7 +8,7 @@ import ( "net/http" "strings" - "github.com/talgat-ruby/exercises-go/exercise7/blogging-platform/pkg/httputils/statusError" + ) func JSON(w http.ResponseWriter, r *http.Request, dst interface{}) error {