Подтвердить что ты не робот

Как получить список общих элементов массива 2 в swift

У меня есть два массива:

fruitsArray = ["apple", "mango", "blueberry", "orange"]
vegArray = ["tomato", "potato", "mango", "blueberry"]

Как я могу получить список общих элементов в этих двух массивах, который дает

ouptput = ["mango", "blueberry"]

Я не могу использовать if contains(array, string), поскольку я хочу сравнить 2 массива. Пожалуйста, помогите

4b9b3361

Ответ 1

Вы также можете использовать filter и contains совместно:

let fruitsArray = ["apple", "mango", "blueberry", "orange"]
let vegArray = ["tomato", "potato", "mango", "blueberry"]

// only Swift 1
let output = fruitsArray.filter{ contains(vegArray, $0) }

// in Swift 2 and above
let output = fruitsArray.filter{ vegArray.contains($0) }
// or
let output = fruitsArray.filter(vegArray.contains)

Set vs Array для одного вычисления общих элементов

Мы рассмотрим следующий фрагмент кода:

let array1: Array = ...
let array2: Array = ...

// `Array`
let commonElements = array1.filter(array2.contains)

// vs `Set`
let commonElements = Array(Set(array1).intersection(Set(array2)))
// or (performance wise equivalent)
let commonElements: Array = Set(array1).filter(Set(array2).contains)

Я сделал несколько (искусственных) тестов с Int и short/long String (от 10 до 100 Character s) (все генерируемые случайным образом). Я всегда использую array1.count == array2.count

Получаю следующие результаты:

Если у вас более critical #(number of) elements предпочтительнее преобразование в Set

data         |  critical #elements
-------------|--------------------
         Int |        ~50
short String |       ~100
 long String |       ~200

Объяснение результатов

Использование подхода Array использует метод "грубой силы", который временная сложность O(N^2) где N = array1.count = array2.count, который находится в контрастирует с подходом Set O(N). Однако преобразование с Array в Set и обратно очень дорого для больших данных, что объясняет увеличение critical #elements для больших типов данных.


Заключение

При малом Array с примерно 100 элементами подход Array хорош, но для более крупных нужно использовать подход Set.

Если вы хотите использовать эти "общие элементы" -операцию несколько раз, желательно использовать Set только, если это возможно (тип элементов должен быть Hashable).

Заключительные замечания

Преобразование из Array в Set является дорогостоящим, в то время как преобразование с Set в Array контрастирует очень недорого.

Использование filter с .filter(array1.contains) выполняется быстрее, чем .filter{ array1.contains($0) }, поскольку:

  • последнее создает новое замыкание (только один раз), тогда как первый передает только указатель на функцию
  • для последнего вызова замыкания создает дополнительный стек стека, который стоит пространство и время ( несколько раз: O(N))

Ответ 2

Преобразуйте их в Set и используйте функцию intersect():

let fruitsArray = ["apple", "mango", "blueberry", "orange"]
let vegArray = ["tomato", "potato", "mango", "blueberry"]
let fruitsSet = Set(fruitsArray)
let vegSet = Set(vegArray)
let output = Array(fruitsSet.intersect(vegSet))

Ответ 3

Вам не нужен набор (как упоминалось выше).

Вместо этого вы можете использовать универсальную функцию, аналогичную используемой Apple в Swift Tour, и, таким образом, избегать кастинга:

func anyCommonElements <T, U where T: SequenceType, U: SequenceType, T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element> (lhs: T, rhs: U) -> Bool {
    for lhsItem in lhs {
        for rhsItem in rhs {
            if lhsItem == rhsItem {
                return true
            }
        }
    }
    return false
}

Эта функция может принимать любые два массива (SequenceTypes), и если любой из их элементов одинаковый, он возвращает true.

Вы можете просто изменить эту общую функцию, чтобы упаковать массив строк и вернуть это вместо.

Например, например:

func arrayOfCommonElements <T, U where T: SequenceType, U: SequenceType, T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element> (lhs: T, rhs: U) -> [T.Generator.Element] {
    var returnArray:[T.Generator.Element] = []
    for lhsItem in lhs {
        for rhsItem in rhs {
            if lhsItem == rhsItem {
                returnArray.append(lhsItem)
            }
        }
    }
    return returnArray
}

Использование:

var one = ["test2", "dog", "cat"]
var other = ["test2", "cat", "dog"]


var result = arrayOfCommonElements(one,other)

print(result) //prints [test2, dog, cat]

Дополнительным преимуществом здесь является то, что эта функция также работает со всеми теми же типизированными массивами. Поэтому, если вам нужно сравнить два массива [myCustomObject], как только они оба будут соответствовать равнозначным, вы все установите! (каламбур)

Изменить: (Для не общих элементов) вы можете сделать что-то вроде этого

func arrayOfNonCommonElements <T, U where T: SequenceType, U: SequenceType, T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element> (lhs: T, rhs: U) -> [T.Generator.Element] {

    var returnArray:[T.Generator.Element] = []
    var found = false

    for lhsItem in lhs {
        for rhsItem in rhs {
            if lhsItem == rhsItem {
                found = true
                break
            }
        }

        if (!found){
            returnArray.append(lhsItem)
        }

        found = false
    }
    for rhsItem in rhs {
        for lhsItem in lhs {
            if rhsItem == lhsItem {
                found = true
                break
            }
        }

        if (!found){
            returnArray.append(rhsItem)
        }

        found = false
    }
    return returnArray
}

Эта реализация является уродливой, хотя.

Ответ 4

Swift 3.0

Используйте фильтр для получения общих элементов из двух массивов.

let fruitsArray = ["apple", "mango", "blueberry", "orange"]
let vegArray = ["tomato", "potato", "mango", "blueberry"]

let newArray  = fruitsArray.filter { (string) -> Bool in
     return vegArray.contains(string)
}
 print(newArray)

// OR
/*let newArray  = fruitsArray.filter{vegArray.contains($0)}*/
//Different Element
/*let newArray  = fruitsArray.filter{!vegArray.contains($0)}*/

Вывод:

[ "манго", "голубика" ]

Ответ 5

Общий метод, вдохновленный Язык Swift Programming (Swift 3):

func commonElements<T: Sequence, U: Sequence>(_ lhs: T, _ rhs: U) -> [T.Iterator.Element]
    where T.Iterator.Element: Equatable, T.Iterator.Element == U.Iterator.Element {
        var common: [T.Iterator.Element] = []

        for lhsItem in lhs {
            for rhsItem in rhs {
                if lhsItem == rhsItem {
                    common.append(lhsItem)
                }
            }
        }
        return common
}

Ответ 6

Следующее работает с быстрым 4:

   let fruitsArray = ["apple", "mango", "blueberry", "orange"]
   let vegArray = ["tomato", "potato", "mango", "blueberry"]

   var someHash: [String: Bool] = [:]

   fruitsArray.forEach { someHash[$0] = true }

   var commonItems = [String]()

   vegArray.forEach { veg in
    if someHash[veg] ?? false {
        commonItems.append(veg)
    }
   }

   print(commonItems)

Ответ 7

Вы даже можете сравнивать объекты внутри массива и отфильтровывать общий массив

struct myStruct
    {
      var userid:String;
      var details:String;
      init() {
        userid = "default value";
        details = "default";
      }

    };
    var f1 = myStruct();
    f1.userid = "1";
    f1.details = "Good boy";

    var f2 = myStruct();
    f2.userid = "2";
    f2.details = "Bad boy";

    var f3 = myStruct();
    f3.userid = "3";
    f3.details = "Gentleman";

    var arrNames1:Array = [f1,f3];

    var arrNames2:Array = [f3,f1,f2];

    let filteredArrayStruct =  arrNames1.filter( { (user: myStruct) -> Bool in
      return arrNames2.contains({ (user1: myStruct) -> Bool in
        return user.userid == user1.userid;
      })
    })
print(filteredArrayStruct)