【问题标题】:How can I filter an array from a array of arrays where all the items in one array matches some of the items in another array?如何从一组数组中过滤一个数组,其中一个数组中的所有项都与另一个数组中的某些项匹配?
【发布时间】:2020-08-31 08:06:17
【问题描述】:

TL/DR

如何过滤一个数组数组以反对一个字符串数组?

如果一个数组中的部分或全部项目存在于另一个数组中,我可以在其中过滤数组数组吗?

因此,例如,将其视为源数组:

arrays := [][]string{
        {"some", "value"},
        {"some", "value", "another"},
        {"value", "another", "test"},
        {"value", "test"},
        {"some", "test"},
    }

如果在数组中找到这里的所有项目,我想通过[]string{"some", "value"} 过滤arrays

预期的输出是

[[some value] [some value another]]

或者,如果我将过滤器更改为[]string{"some", "test"},则预期值为[[some test]]

我可以在我的测试代码中完全正确地理解逻辑

package main

import "fmt"

func inArray(s string, arr []string) bool {
    for _, a := range arr {
        if s == a {
            return true
        }
    }
    return false
}

func main() {
    arrays := [][]string{
        {"some", "value"},
        {"some", "value", "another"},
        {"value", "another", "test"},
        {"value", "test"},
        {"some", "test"},
    }
    filterBy := []string{"some", "value"}
    hold := make([][]string, 0)
    // Ignore this because it doesnt work as expected
    for _, arr := range arrays {
        for _, f := range filterBy {
            if ok := inArray(f, arr); ok {
                hold = append(hold, arr)
            }
        }
    }
    fmt.Println(hold)
}

【问题讨论】:

标签: go


【解决方案1】:

检查在每个数组中找到的所有过滤器字符串,然后决定追加到切片中。我还添加了一些 cmets。

 hold := make([][]string, 0)
 for _, arr := range arrays {
    isValid := true // Initialy set true
    for _, f := range filterBy {
        if ok := inArray(f, arr); !ok {
            isValid = false // if any filter string does match then make false and break
            break
        }
    }
    // If all filter string found the isValid is true, then append in slice
    if(isValid){ 
       hold = append(hold, arr)
    }
}

完整代码在 go playground here

但有效的解决方案是

filterByMap := make(map[string]bool)
// Create a map for filter slice
for _, a := range filterBy {
    filterByMap[a] = true
}
for _, arr := range arrays {
    cnt := 0 // Initial the counter
    for _, a := range arr {
      if filterByMap[a] {
          cnt++  // Increment the counter if filter string found
      }
    }
    // Check if all filter string found ? Append if yes
    if(cnt >= len(filterBy)){ 
         hold = append(hold, arr)
    }
}

完整代码here

【讨论】:

  • 你是绝对正确的——你的解决方案更有效,但假设每根针在大海捞针中只出现一次。一些反例请参见play.golang.org/p/8RsN-mhzYpp
  • @PaSTE 我不知道 OP 是否也需要这方面,但你是对的
【解决方案2】:

inArray 函数中的逻辑对于检查单个针 s string 是否在大海捞针 arr []string 是正确的。如果您想扩展它以检查 所有ss []string 是否存在于大海捞针 arr []string 中,那么您至少还需要遍历针。这是一个例子:

func allInArray(ss []string, arr []string) bool {
    for _, s := range ss {
        if !inArray(s, arr) {
            return false
        }
    }
    return true
}

Here is a working example in the playgound.

当然,这是非常低效的,因为它在大海捞针arr 上循环的次数与ss 中的针数一样多。为了提高效率,您可以对 haystack 进行预处理,将其转换为 map[string]struct{},然后根据地图的键检查针头,如下所示:

func allInArray(ss []string, arr []string) bool {
    present := make(map[string]struct{})
    for _, a := range arr {
        present[a] = struct{}{}
    }
    for _, s := range ss {
        if _, ok := present[s]; !ok {
            return false
        }
    }
    return true
}

Here is a working example in the playground.

这会遍历arr 一次以创建查找映射,然后遍历ss 一次,利用映射的恒定查找时间来检查ss 的元素是否存在于@987654336 中@。

【讨论】:

  • 为每个数组创建映射是一种低效的解决方案,而是创建过滤器数组的映射更有效
【解决方案3】:

您可以使用我创建的支持 Some() 和 Every() 的 https://github.com/ledongthuc/goterators 来重用聚合和转换函数。 但是对于您的情况,我认为您需要将同一个库与 Filter() 和 Exist() 结合使用。

package main

import (
    "fmt"

    "github.com/ledongthuc/goterators"
)

func main() {
    arrays := [][]string{
        {"some", "value"},
        {"some", "value", "another"},
        {"value", "another", "test"},
        {"value", "test"},
        {"some", "test"},
    }

    expected := []string{"some", "value"}
    filteredItems := goterators.Filter(arrays, func(itemList []string) bool {
        for _, expectedItem := range expected {
            if !goterators.Exist(itemList, expectedItem) {
                return false
            }
        }
        return true
    })

    fmt.Println(filteredItems)
}

此库需要 Go 1.18 才能使用支持您要使用的泛型 + 动态类型。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多