Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.DS_Store
11 changes: 11 additions & 0 deletions part1/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module uniq

go 1.22.0

require github.com/stretchr/testify v1.9.0

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
10 changes: 10 additions & 0 deletions part1/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
7 changes: 7 additions & 0 deletions part1/input.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
We love music.
I love music.
They love music.

I love music of Kartik.
We love music of Kartik.
Thanks.
48 changes: 48 additions & 0 deletions part1/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package main

import (
"fmt"
"os"
"uniq/reader"
"uniq/settings"
"uniq/uniq"
"uniq/writer"
)

func main() {

var mas []string
var err error

options, err := settings.GetOptions()
if err != nil {
fmt.Println(err)
os.Exit(1)
}

in, err := settings.SetInput()
if err != nil {
fmt.Println(err)
os.Exit(1)
}

out, err := settings.SetOutput()
if err != nil {
fmt.Println(err)
os.Exit(1)
}

mas, err = reader.Reader(in)
if err != nil {
fmt.Println(err)
os.Exit(1)
}

res := uniq.Uniq(mas, options)

err = writer.Writer(res, out)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
4 changes: 4 additions & 0 deletions part1/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
We love music.

I love music of Kartik.
Thanks.
21 changes: 21 additions & 0 deletions part1/reader/reader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package reader

import (
"bufio"
"fmt"
"io"
)

func Reader(input io.Reader) ([]string, error) {
in := bufio.NewScanner(input)
var masOfLines []string = make([]string, 0)
for in.Scan() {
txt := in.Text()
masOfLines = append(masOfLines, txt)
}
if len(masOfLines) == 0 {
return nil, fmt.Errorf("Empty stream")
} else {
return masOfLines, nil
}
}
14 changes: 14 additions & 0 deletions part1/reader/reader_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package reader

import (
"bytes"
"github.com/stretchr/testify/assert"
"testing"
)

func TestFail(t *testing.T) {
var input = ``
in := bytes.NewBufferString(input)
_, err := Reader(in)
assert.NotNil(t, err)
}
154 changes: 154 additions & 0 deletions part1/settings/settings.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package settings

import (
"flag"
"fmt"
"io"
"os"
"strconv"
"strings"
)

type Options struct {
WriteRepeatedLines bool
CountNumOfRepeats bool
OnlyUnicLines bool
Compare func(string, string, int, int) bool
IgnoreNumOfChars int
IgnoreNumOfFields int
IgnoreCase bool
}

func SetInput() (io.Reader, error) {
if flag.Arg(0) != "" {
file, err := os.Open(flag.Arg(0))
if err != nil {
return nil, err
}
return file, nil
} else {
return os.Stdin, nil
}
}
func SetOutput() (io.Writer, error) {
if flag.Arg(1) != "" {
file, err := os.OpenFile(flag.Arg(1), os.O_WRONLY|os.O_CREATE, 0777)
if err != nil {
return nil, err
}
return file, nil
} else {
return os.Stdout, nil
}
}
func checkRightUsage(options Options) error {
var booleans []bool = []bool{options.WriteRepeatedLines, options.CountNumOfRepeats, options.OnlyUnicLines}
count := 0
for _, value := range booleans {
if value {
count++
}
}
if count > 1 {
usagesMas := rightUsage(options, booleans)
var usages string
for i := range usagesMas {
usages += "\n" + usagesMas[i]
}
return fmt.Errorf("use only one option: -d or -c or -u \ntry one of this usages:" + usages)
}
return nil
}
func GetOptions() (Options, error) {
var flagD bool
var flagC bool
var flagU bool
var flagI bool
var flagF int
var flagS int
flag.BoolVar(&flagD, "d", false, "only repeat")
flag.BoolVar(&flagC, "c", false, "count")
flag.BoolVar(&flagU, "u", false, "not repeat")
flag.BoolVar(&flagI, "i", false, "ignore letter case")
flag.IntVar(&flagF, "f", 0, "ignore first n fields")
flag.IntVar(&flagS, "s", 0, "ignore first n chars")
flag.Parse()
options := Options{
WriteRepeatedLines: flagD,
CountNumOfRepeats: flagC,
OnlyUnicLines: flagU,
IgnoreNumOfChars: flagS,
IgnoreNumOfFields: flagF,
IgnoreCase: flagI,
}
err := checkRightUsage(options)
if err != nil {
return Options{}, err
}
if flagI {
options.Compare = EqualWithoutCase
} else {
options.Compare = EqualWithCase
}
return options, nil
}
func rightUsage(options Options, booleans []bool) []string {
str := "go run main.go "
var flags []string = []string{"-d", "-c", "-u"}
var strings []string = make([]string, 0)
for i, value := range booleans {
if value {
strings = append(strings, str)
strings[i] += flags[i]
}
}
if options.IgnoreNumOfChars > 0 {
for i := range strings {
strings[i] = strings[i] + " -s " + strconv.Itoa(options.IgnoreNumOfChars)
}
}
if options.IgnoreNumOfFields > 0 {
for i := range strings {
strings[i] = strings[i] + " -f " + strconv.Itoa(options.IgnoreNumOfFields)
}
}
if options.IgnoreCase {
for i := range strings {
strings[i] = strings[i] + " -i "
}
}
if flag.Arg(0) != "" {
for i := range strings {
strings[i] = strings[i] + " " + flag.Arg(0)
}
}
if flag.Arg(1) != "" {
for i := range strings {
strings[i] = strings[i] + " " + flag.Arg(1)
}
}
return strings
}
func cutFieldsAndChars(str string, ignoredFields int, ignoredChars int) string {
words := strings.Fields(str)
if len(words) < ignoredFields {
return ""
}
newStr := strings.Join(words[ignoredFields:], " ")
if len(newStr) < ignoredChars {
return ""
}
return strings.Join(words[ignoredFields:], " ")[ignoredChars:]
}

func EqualWithoutCase(str1 string, str2 string, ignoredFields int, ignoredChars int) bool {
newStr1 := cutFieldsAndChars(str1, ignoredFields, ignoredChars)
newStr2 := cutFieldsAndChars(str2, ignoredFields, ignoredChars)
return strings.ToLower(newStr1) == strings.ToLower(newStr2)
}

func EqualWithCase(str1 string, str2 string, ignoredFields int, ignoredChars int) bool {
newStr1 := cutFieldsAndChars(str1, ignoredFields, ignoredChars)
newStr2 := cutFieldsAndChars(str2, ignoredFields, ignoredChars)
return newStr1 == newStr2
}
27 changes: 27 additions & 0 deletions part1/settings/settings_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package settings

import (
"github.com/stretchr/testify/assert"
"testing"
)

func TestCheckRightUsage(t *testing.T) {
assert.NotNil(t, checkRightUsage(Options{WriteRepeatedLines: true, CountNumOfRepeats: true, OnlyUnicLines: true, IgnoreNumOfChars: 3}))
}
func TestRightUsage(t *testing.T) {
assert.Equal(t, rightUsage(Options{WriteRepeatedLines: true, CountNumOfRepeats: true, OnlyUnicLines: true, IgnoreNumOfChars: 3}, []bool{true, true, true}), []string{"go run main.go -d -s 3", "go run main.go -c -s 3", "go run main.go -u -s 3"}, "must be equal")
assert.Equal(t, rightUsage(Options{WriteRepeatedLines: true, CountNumOfRepeats: true, IgnoreNumOfFields: 6}, []bool{true, true, false}), []string{"go run main.go -d -f 6", "go run main.go -c -f 6"}, "must be equal")
}
func TestCutFieldsAndChars(t *testing.T) {
assert.Equal(t, cutFieldsAndChars("", 1, 1), "", "must be equal")
assert.Equal(t, cutFieldsAndChars("a", 3, 0), "", "must be equal")
assert.Equal(t, cutFieldsAndChars("aaa", 0, 100), "", "must be equal")
}
func TestEqualWithoutCase(t *testing.T) {
assert.True(t, EqualWithoutCase("hello", "HelLo", 0, 0))
assert.False(t, EqualWithoutCase("helo", "HelLo", 0, 0))
}
func TestEqualWithCase(t *testing.T) {
assert.True(t, EqualWithCase("hello", "hello", 1, 0))
assert.False(t, EqualWithCase("hello", "HelLo", 0, 0))
}
59 changes: 59 additions & 0 deletions part1/uniq/uniq.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package uniq

import (
"strconv"
"uniq/settings"
)

type lineInfo struct {
str string
count int
}

func Uniq(masOfLines []string, options settings.Options) []string {
var lineInfoMas []lineInfo = make([]lineInfo, 0)
var ignoredChars int = options.IgnoreNumOfChars
var ignoredFields int = options.IgnoreNumOfFields

for i := 0; i < len(masOfLines); i++ {
str := masOfLines[i]
if i == 0 {
lineInfoMas = append(lineInfoMas, lineInfo{str, 1})
continue
}
prev := masOfLines[i-1]
if options.Compare(str, prev, ignoredFields, ignoredChars) {
lineInfoMas[len(lineInfoMas)-1].count += 1
} else {
lineInfoMas = append(lineInfoMas, lineInfo{str, 1})
}
}

var resultMas []string = make([]string, 0)

if options.WriteRepeatedLines {
for i := 0; i < len(lineInfoMas); i++ {
num := lineInfoMas[i].count
if num > 1 {
resultMas = append(resultMas, lineInfoMas[i].str)
}
}
} else if options.OnlyUnicLines {
for i := 0; i < len(lineInfoMas); i++ {
num := lineInfoMas[i].count
if num == 1 {
resultMas = append(resultMas, lineInfoMas[i].str)
}
}
} else if options.CountNumOfRepeats {
for i := 0; i < len(lineInfoMas); i++ {
num := lineInfoMas[i].count
resultMas = append(resultMas, strconv.Itoa(num)+" "+lineInfoMas[i].str)
}
} else {
for i := 0; i < len(lineInfoMas); i++ {
resultMas = append(resultMas, lineInfoMas[i].str)
}
}
return resultMas
}
26 changes: 26 additions & 0 deletions part1/uniq/uniq_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package uniq

import (
"github.com/stretchr/testify/assert"
"testing"
"uniq/settings"
)

func TestOK(t *testing.T) {
tests := []struct {
str []string
option settings.Options
expected []string // ожидаемый результат
}{
{[]string{"I love music.", "I love music.", "I love music.", "", "I love music of Kartik.", "I love music of Kartik.", "Thanks.", "I love music of Kartik.", "I love music of Kartik."}, settings.Options{Compare: settings.EqualWithCase}, []string{"I love music.", "", "I love music of Kartik.", "Thanks.", "I love music of Kartik."}},
{[]string{"I love music.", "I love music.", "I love music.", "", "I love music of Kartik.", "I love music of Kartik.", "Thanks.", "I love music of Kartik.", "I love music of Kartik."}, settings.Options{CountNumOfRepeats: true, Compare: settings.EqualWithCase}, []string{"3 I love music.", "1 ", "2 I love music of Kartik.", "1 Thanks.", "2 I love music of Kartik."}},
{[]string{"I love music.", "I love music.", "I love music.", "", "I love music of Kartik.", "I love music of Kartik.", "Thanks.", "I love music of Kartik.", "I love music of Kartik."}, settings.Options{WriteRepeatedLines: true, Compare: settings.EqualWithCase}, []string{"I love music.", "I love music of Kartik.", "I love music of Kartik."}},
{[]string{"I love music.", "I love music.", "I love music.", "", "I love music of Kartik.", "I love music of Kartik.", "Thanks.", "I love music of Kartik.", "I love music of Kartik."}, settings.Options{OnlyUnicLines: true, Compare: settings.EqualWithCase}, []string{"", "Thanks."}},
{[]string{"I LOVE MUSIC.", "I love music.", "I LoVe MuSiC.", "", "I love MuSIC of Kartik.", "I love music of kartik.", "Thanks.", "I love music of kartik.", "I love MuSIC of Kartik."}, settings.Options{Compare: settings.EqualWithoutCase}, []string{"I LOVE MUSIC.", "", "I love MuSIC of Kartik.", "Thanks.", "I love music of kartik."}},
{[]string{"We love music.", "I love music.", "They love music.", "", "I love music of Kartik.", "We love music of Kartik.", "Thanks."}, settings.Options{Compare: settings.EqualWithCase, IgnoreNumOfFields: 1}, []string{"We love music.", "", "I love music of Kartik.", "Thanks."}},
{[]string{"I love music.", "A love music.", "C love music.", "", "I love music of Kartik.", "We love music of Kartik.", "Thanks."}, settings.Options{Compare: settings.EqualWithCase, IgnoreNumOfChars: 1}, []string{"I love music.", "", "I love music of Kartik.", "We love music of Kartik.", "Thanks."}},
}
for i := range tests {
assert.Equal(t, Uniq(tests[i].str, tests[i].option), tests[i].expected, "must be equal")
}
}
Loading