Remove duplicate words from a string in golang - go

i have a string in golang :
"hi hi hi ho ho hello"
I would like to remove duplicates word to keep only one to obtain this :
"hi ho hello"

There are multiple way to achive this. One is this:
import "strings"
func Dedup(input string) string {
unique := []string{}
words := strings.Split(input, " ")
for _, word := range words {
// If we alredy have this word, skip.
if contains(unique, word) {
continue
}
unique = append(unique, word)
}
return strings.Join(unique, " ")
}
func contains(strs []string, str string) bool {
for _, s := range strs {
if s == str {
return true
}
}
return false
}

package main
import "fmt"
func removeDuplicates(arr []string) []string {
words_string := map[string]bool{}
for i:= range arr {
words_string[arr[i]] = true
}
desired_output := []string{} // Keep all keys from the map into a slice.
for j, _ := range words_string {
desired_output = append(desired_output, j)
}
return desired_output
}
func main() {
arr := []string{"hi", "hi", "hi", "ho", "ho", "hello"}
fmt.Println(arr)
desired_output := removeDuplicates(arr) // Remove the duplicates
fmt.Println(desired_output)
}

Related

How to convert strings to lower case in GO?

I am new to the language GO and working on an assignment where i should write a code that return the word frequencies of the text. However I know that the words 'Hello', 'HELLO' and 'hello' are all counted as 'hello', so I need to convert all strings to lower case.
I know that I should use strings.ToLower(), however I dont know where I should Included that in the class. Can someone please help me?
package main
import (
"fmt"
"io/ioutil"
"log"
"strings"
"time"
)
const DataFile = "loremipsum.txt"
// Return the word frequencies of the text argument.
func WordCount(text string) map[string]int {
fregs := make(map[string]int)
words := strings.Fields(text)
for _, word := range words {
fregs[word] += 1
}
return fregs
}
// Benchmark how long it takes to count word frequencies in text numRuns times.
//
// Return the total time elapsed.
func benchmark(text string, numRuns int) int64 {
start := time.Now()
for i := 0; i < numRuns; i++ {
WordCount(text)
}
runtimeMillis := time.Since(start).Nanoseconds() / 1e6
return runtimeMillis
}
// Print the results of a benchmark
func printResults(runtimeMillis int64, numRuns int) {
fmt.Printf("amount of runs: %d\n", numRuns)
fmt.Printf("total time: %d ms\n", runtimeMillis)
average := float64(runtimeMillis) / float64(numRuns)
fmt.Printf("average time/run: %.2f ms\n", average)
}
func main() {
// read in DataFile as a string called data
data, err:= ioutil.ReadFile("loremipsum.txt")
if err != nil {
log.Fatal(err)
}
// Convert []byte to string and print to screen
text := string(data)
fmt.Println(text)
fmt.Printf("%#v",WordCount(string(data)))
numRuns := 100
runtimeMillis := benchmark(string(data), numRuns)
printResults(runtimeMillis, numRuns)
}
You should convert words to lowercase when you are using them as map key
for _, word := range words {
fregs[strings.ToLower(word)] += 1
}
I get [a:822 a.:110 I want all a in the same. How do i a change the code so that a and a. is the same? – hello123
You need to carefully define a word. For example, a string of consecutive letters and numbers converted to lowercase.
func WordCount(s string) map[string]int {
wordFunc := func(r rune) bool {
return !unicode.IsLetter(r) && !unicode.IsNumber(r)
}
counts := make(map[string]int)
for _, word := range strings.FieldsFunc(s, wordFunc) {
counts[strings.ToLower(word)]++
}
return counts
}
to remove all non-word characters you could use a regular expression:
package main
import (
"bufio"
"fmt"
"log"
"regexp"
"strings"
)
func main() {
str1 := "This is some text! I want to count each word. Is it cool?"
re, err := regexp.Compile(`[^\w]`)
if err != nil {
log.Fatal(err)
}
str1 = re.ReplaceAllString(str1, " ")
scanner := bufio.NewScanner(strings.NewReader(str1))
scanner.Split(bufio.ScanWords)
for scanner.Scan() {
fmt.Println(strings.ToLower(scanner.Text()))
}
}
See strings.EqualFold.
Here is an example.

Ranging over a slice

my problem is that Im only allowed to use the command PrintRune, i must range over a string and print one by one the characters of any string
package piscine
import "github.com/01-edu/z01"
func PrintStr(s string) {
slice := []string{
s,
}
for x, word := range slice {
z01.PrintRune(rune(word[x]))
}
}
here's my code, this only prints the first character of the string, how can i make the slice continue until the end of the given string please ?
Here is the code snippets:
package piscine
import "github.com/01-edu/z01"
func PrintStr(s string) {
slice := []string{
s,
}
for _, word := range slice {
for _, r := range word {
z01.PrintRune(rune(r))
}
}
}

Trying to reverse strings but there are spaces after reversing the string

I was trying to write a stringReverse function which reverses the given string:
func reverseString(s string) []rune
well, i did succeed but the problem is there are spaces after reversing it.
Input: "Hello, 世界"
Output: [界 世 , o l l e H]
code of function :
func reverseString(s string) []rune {
// sir: string in rune
sir := []rune(s)
for i, _ := range sir {
sir[i], sir[len(sir)-i-1] = sir[len(sir)-i-1], sir[i]
if i >= len(sir)-i-1 {
return sir
}
}
return sir
}
i did not do any error implementation since i am new to go.
Thanks for your help.
To reverse strings,you can declare an empty string and then start appending the characters from the end, one by one.Here is a sample code for the same logic:
package main
import "fmt"
func reverse(str string) (result string) {
for _, v := range str {
result = string(v) + result
}
return
}
func main() {
runeStr := "Hello, 世界"
fmt.Println(runeStr)
runeRev := reverse(runeStr)
fmt.Println(runeRev)
}
Output:
Hello, 世界
界世 ,olleH
try to
func reverseString(s string) []rune {
// sir: string in rune
sir := []rune(s)
for i, _ := range sir {
sir[i], sir[len(sir)-i-1] = sir[len(sir)-i-1], sir[i]
if i+1 >= len(sir)/2 {
return sir
}
}
return sir
}

How to use random arguments with the help of flags in Golang

I want the input from the console to be of anonymous parameters.
My current way of execution is
./app -infc=eth0 -ip=192.168.0.1
I don't want this as I need this app to be universal so that I can use it for other purposes as well.
I want the CLI to be like this
./app -firstparam={{infc},eth0} -secondparam={{ip},192.168.0.1}
So this should basically work by reading the two columns in the parameters.
So it should parse the parameters as a an internal key value pair
Need help on how to store each of the parameter as a key value pair and later use them individually
Here's a barebones example to give you an idea how to process os.Args
$ go run main.go --foo asdf --bar xxx --baz ccc
map[--foo:asdf --bar:xxx --baz:ccc]
jsandrew-Mac:osarg jsandrew$ cat main.go
package main
import (
"fmt"
"os"
)
func manyRandomArg() map[string]string {
rv := make(map[string]string)
for ix, x := range os.Args {
if x[:2] == "--" {
rv[x] = os.Args[ix+1]
}
}
return rv
}
func main() {
fmt.Printf("%v\n", manyRandomArg())
}
solved it thanks to #Vorsprung
package main
import (
"fmt"
"io/ioutil"
"log"
"os"
"regexp"
"strings"
)
var key0, key1, key2, key3, key4, filename string
var fileext = regexp.MustCompile(`([a-z]+)\.yaml`)
func manyRandomArg() map[string]string {
rv := make(map[string]string)
for ix, x := range os.Args {
if x[:2] == "--" {
rv[x] = os.Args[ix+1]
}
}
return rv
}
func main() {
fmt.Printf("\n%v\n", manyRandomArg())
readargs()
}
func readargs() {
rv := manyRandomArg()
keys := make([]string, 0, len(rv))
for key, _ := range rv {
keys = append(keys, key)
}
// Convert map to slice of values.
values := []string{}
for _, value := range rv {
values = append(values, value)
}
for keys, values := range rv {
fmt.Printf("key[%s] value[%s]\n", keys, values)
}
if fileext.MatchString(values[0]) {
fmt.Printf("Value %s\n", values[0])
filename = values[0]
} else if fileext.MatchString(values[1]) {
fmt.Printf("Value %s\n", values[1])
filename = values[1]
} else if fileext.MatchString(values[2]) {
fmt.Printf("Value %s\n", values[2])
filename = values[2]
} else if fileext.MatchString(values[3]) {
fmt.Printf("Value %s\n", values[3])
filename = values[3]
} else if fileext.MatchString(values[4]) {
fmt.Printf("Value %s\n", values[4])
filename = values[4]
} else {
log.Fatal("index 4 fail")
os.Exit(1)
}
b, err := ioutil.ReadFile(filename) // just pass the file name
if err != nil {
fmt.Print(err)
}
str := string(b) // convert content to a 'string'
key0 = trimLeftChars(keys[0], 2)
key1 = trimLeftChars(keys[1], 2)
key2 = trimLeftChars(keys[2], 2)
key3 = trimLeftChars(keys[3], 2)
key4 = trimLeftChars(keys[4], 2)
// Create replacer with pairs as arguments.
r := strings.NewReplacer(key0, values[0], key1, values[1], key2, values[2], key3, values[3], key4, values[4])
// Replace all pairs.
result := r.Replace(str)
fmt.Println(result)
newContents := []byte(result)
err = ioutil.WriteFile("new3.yaml", newContents, 0664)
if err != nil {
panic(err)
}
}
func trimLeftChars(s string, n int) string {
m := 0
for i := range s {
if m >= n {
return s[i:]
}
m++
}
return s[:0]
}

Convert slice of string input from console to slice of numbers

I'm trying to write a Go script that takes in as many lines of comma-separated coordinates as the user wishes, split and convert the string of coordinates to float64, store each line as a slice, and then append each slice in a slice of slices for later usage.
Example inputs are:
1.1,2.2,3.3
3.14,0,5.16
Example outputs are:
[[1.1 2.2 3.3],[3.14 0 5.16]]
The equivalent in Python is
def get_input():
print("Please enter comma separated coordinates:")
lines = []
while True:
line = input()
if line:
line = [float(x) for x in line.replace(" ", "").split(",")]
lines.append(line)
else:
break
return lines
But what I wrote in Go seems way too long (pasted below), and I'm creating a lot of variables without the ability to change variable type as in Python. Since I literally just started writing Golang to learn it, I fear my script is long as I'm trying to convert Python thinking into Go. Therefore, I would like to ask for some advice as to how to write this script shorter and more concise in Go style? Thank you.
package main
import (
"fmt"
"os"
"bufio"
"strings"
"strconv"
)
func main() {
inputs := get_input()
fmt.Println(inputs)
}
func get_input() [][]float64 {
fmt.Println("Please enter comma separated coordinates: ")
var inputs [][]float64
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
if len(scanner.Text()) > 0 {
raw_input := strings.Replace(scanner.Text(), " ", "", -1)
input := strings.Split(raw_input, ",")
converted_input := str2float(input)
inputs = append(inputs, converted_input)
} else {
break
}
}
return inputs
}
func str2float(records []string) []float64 {
var float_slice []float64
for _, v := range records {
if s, err := strconv.ParseFloat(v, 64); err == nil {
float_slice = append(float_slice, s)
}
}
return float_slice
}
Using only string functions:
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
var result [][]float64
var txt string
for scanner.Scan() {
txt = scanner.Text()
if len(txt) > 0 {
values := strings.Split(txt, ",")
var row []float64
for _, v := range values {
fl, err := strconv.ParseFloat(strings.Trim(v, " "), 64)
if err != nil {
panic(fmt.Sprintf("Incorrect value for float64 '%v'", v))
}
row = append(row, fl)
}
result = append(result, row)
}
}
fmt.Printf("Result: %v\n", result)
}
Run:
$ printf "1.1,2.2,3.3
3.14,0,5.16
2,45,76.0, 45 , 69" | go run experiment2.go
Result: [[1.1 2.2 3.3] [3.14 0 5.16] [2 45 76 45 69]]
With given input, you can concatenate them to make a JSON string and then unmarshal (deserialize) that:
func main() {
var lines []string
for {
var line string
fmt.Scanln(&line)
if line == "" {
break
}
lines = append(lines, "["+line+"]")
}
all := "[" + strings.Join(lines, ",") + "]"
inputs := [][]float64{}
if err := json.Unmarshal([]byte(all), &inputs); err != nil {
fmt.Println(err)
return
}
fmt.Println(inputs)
}

Resources