如何在Golang中找到两片字符串之间的区别?

时间:2013-10-15 05:59:28

标签: go

这是我想要的结果

slice1 := []string{"foo", "bar","hello"}
slice2 := []string{"foo", "bar"}

difference(slice1, slice2)
=> ["hello"]

我正在寻找两个字符串切片之间的区别!

9 个答案:

答案 0 :(得分:24)

假设Go贴图是~O(1),这里是一个~O(n)差分函数,适用于未排序的切片。

// difference returns the elements in `a` that aren't in `b`.
func difference(a, b []string) []string {
    mb := make(map[string]struct{}, len(b))
    for _, x := range b {
        mb[x] = struct{}{}
    }
    var diff []string
    for _, x := range a {
        if _, found := mb[x]; !found {
            diff = append(diff, x)
        }
    }
    return diff
}

答案 1 :(得分:14)

根据切片的大小,不同的解决方案可能是最好的。

我的回答假设顺序无关紧要。

使用简单循环,仅用于较小的切片:

package main

import "fmt"

func difference(slice1 []string, slice2 []string) []string {
    var diff []string

    // Loop two times, first to find slice1 strings not in slice2,
    // second loop to find slice2 strings not in slice1
    for i := 0; i < 2; i++ {
        for _, s1 := range slice1 {
            found := false
            for _, s2 := range slice2 {
                if s1 == s2 {
                    found = true
                    break
                }
            }
            // String not found. We add it to return slice
            if !found {
                diff = append(diff, s1)
            }
        }
        // Swap the slices, only if it was the first loop
        if i == 0 {
            slice1, slice2 = slice2, slice1
        }
    }

    return diff
}

func main() {
    slice1 := []string{"foo", "bar", "hello"}
    slice2 := []string{"foo", "world", "bar", "foo"}

    fmt.Printf("%+v\n", difference(slice1, slice2))
}

输出:

[hello world]

游乐场:http://play.golang.org/p/KHTmJcR4rg

答案 2 :(得分:12)

我使用地图来解决这个问题

package main

import "fmt"

func main() {
    slice1 := []string{"foo", "bar","hello"}
    slice2 := []string{"foo", "bar","world"}

    diffStr := difference(slice1, slice2)

    for _, diffVal := range diffStr {
        fmt.Println(diffVal)
    }

}

func difference(slice1 []string, slice2 []string) ([]string){
    diffStr := []string{}
    m :=map [string]int{}

    for _, s1Val := range slice1 {
        m[s1Val] = 1
    }
    for _, s2Val := range slice2 {
        m[s2Val] = m[s2Val] + 1
    }

    for mKey, mVal := range m {
        if mVal==1 {
            diffStr = append(diffStr, mKey)
        }
    }

    return diffStr
}

输出:
你好
世界

答案 3 :(得分:1)

func unique(slice []string) []string {
    encountered := map[string]int{}
    diff := []string{}

    for _, v := range slice {
        encountered[v] = encountered[v]+1
    }

    for _, v := range slice {
        if encountered[v] == 1 {
        diff = append(diff, v)
        }
    }
    return diff
}

func main() {
    slice1 := []string{"hello", "michael", "dorner"}
    slice2 := []string{"hello", "michael"}
    slice3 := []string{}
    fmt.Println(unique(append(slice1, slice2...))) // [dorner]
    fmt.Println(unique(append(slice2, slice3...))) // [michael michael]
}

答案 4 :(得分:0)

作为mentioned by ANisus,不同的方法适合不同大小的输入切片。此解决方案将在与输入大小无关的线性时间O(n)中工作,但假定“相等”包括索引位置。

因此,在OP的例子中:

slice1 := []string{"foo", "bar","hello"}
slice2 := []string{"foo", "bar"}

条目foobar不等,不仅仅是因为价值,还因为它们在切片中的索引。

鉴于这些条件,您可以执行以下操作:

package main

import "fmt"

func difference(s1, s2 []string) string {
    var (
        lenMin  int
        longest []string
        out     string
    )
    // Determine the shortest length and the longest slice
    if len(s1) < len(s2) {
        lenMin = len(s1)
        longest = s2
    } else {
        lenMin = len(s2)
        longest = s1
    }
    // compare common indeces
    for i := 0; i < lenMin; i++ {
        if s1[i] != s2[i] {
            out += fmt.Sprintf("=>\t%s\t%s\n", s1[i], s2[i])
        }
    }
    // add indeces not in common
    for _, v := range longest[lenMin:] {
        out += fmt.Sprintf("=>\t%s\n", v)
    }
    return out
}

func main() {
    slice1 := []string{"foo", "bar", "hello"}
    slice2 := []string{"foo", "bar"}
    fmt.Print(difference(slice1, slice2))
}

产地:

  
    

=&GT;喂

  

Playground

如果您将切片更改为:

func main() {
    slice1 := []string{"foo", "baz", "hello"}
    slice2 := []string{"foo", "bar"}    
    fmt.Print(difference(slice1, slice2))
}

它会产生:

  
    

=&GT;巴兹吧
    =&GT;喂

  

答案 5 :(得分:0)

如果切片包含重复的元素,此处的大多数其他解决方案将无法返回正确的答案。

如果切片已排序,则此解决方案为O(n)时间和O(n)空间;如果未排序,则为O(n * log(n))时间O(n)空间,但具有不错的属性实际上是正确的。

     jlink \
        --add-modules java.base,java.logging,java.xml,jdk.unsupported,java.sql,java.naming,java.desktop,java.management,java.security.jgss,java.instrument,jdk.management,java.net.http \
        --module-path $(find $JAVA_HOME -name lib -type d) \
        --output ~/jre

如果您确定已对切片进行了排序,则可以删除对func diff(a, b []string) []string { a = sortIfNeeded(a) b = sortIfNeeded(b) var d []string i, j := 0, 0 for i < len(a) && j < len(b) { c := strings.Compare(a[i], b[j]) if c == 0 { i++ j++ } else if c < 0 { d = append(d, a[i]) i++ } else { d = append(d, b[j]) j++ } } d = append(d, a[i:len(a)]...) d = append(d, b[j:len(b)]...) return d } func sortIfNeeded(a []string) []string { if sort.StringsAreSorted(a) { return a } s := append(a[:0:0], a...) sort.Strings(s) return s } 的调用(sortIfNeeded中具有防御性的切片副本的原因是,排序是就地完成的,因此我们将修改传递到sortIfNeeded的切片。

有关面对重复条目的正确性的测试,请参见https://play.golang.org/p/lH-5L0aL1qr

答案 6 :(得分:0)

func diff(a, b []string) []string {
    temp := map[string]int{}
    for _, s := range a {
        temp[s]++
    }
    for _, s := range b {
        temp[s]--
    }

    var result []string
    for s, v := range temp {
        if v != 0 {
            result = append(result, s)
        }
    }
    return result
}

如果要处理重复的字符串,则映射中的v可以做到。您可以选择a.Remove(b)v>0)或b.Remove(a)v<0

答案 7 :(得分:0)

这是我的解决方案,使用package sort

  • 您只能在first中找到不在second中的元素:secondfirst的{​​{3}}或first \ { {1}}。
  • 您对second进行了分类!对于某些用例,这可能是个问题。
second

relative complement

答案 8 :(得分:-1)

下面的代码给出了字符串之间的绝对差异,而与顺序无关。空间复杂度O(n)和时间复杂度O(n)。

// difference returns the elements in a that aren't in b
func difference(a, b string) string {
    longest, shortest := longestString(&a, &b)
    var builder strings.Builder
    var mem = make(map[rune]bool)
    for _, s := range longest {
        mem[s] = true
    }
    for _, s := range shortest {
        if _, ok := mem[s]; ok {
            mem[s] = false
        }
    }
    for k, v := range mem {
        if v == true {
            builder.WriteRune(k)
        }
    }
    return builder.String()
}
func longestString(a *string, b *string) ([]rune, []rune) {
    if len(*a) > len(*b) {
        return []rune(*a), []rune(*b)
    }
    return []rune(*b), []rune(*a)
}