2016-02-08 2 views
3

У меня есть простой массив пользовательских объектов.Уменьшить экземпляры объекта в массиве по правилу

Я хотел бы уменьшить массив до одного экземпляра каждого цвета, выбранного наибольшим размером.

Решения, которые я придумал, кажутся довольно громоздкими, что было бы лучшим подходом, я пробовал смотреть на сокращение и фильтрацию, но не мог понять, как подать заявку здесь.

class foo { 

    var color: String 
    var size: Int 
    var shape: String 

    init(color:String, size:Int, shape:String){ 
     self.color = color 
     self.size = size 
     self.shape = shape 
    } 

} 

var array = [foo]() 

array.append(foo(color: "Blue", size: 2, shape: "Round")) 
array.append(foo(color: "Red", size: 3, shape: "Square")) 
array.append(foo(color: "Blue", size: 5, shape: "Round")) 
array.append(foo(color: "Yellow", size: 1, shape: "Triangle")) 
array.append(foo(color: "Blue", size: 1, shape: "Hexagon")) 
+0

я видел вопрос, что было предложено. Я попытался адаптировать решения к моему примеру, но для меня это не просто, так как это не просто массив ints. – Magrafear

+2

Вы не указали свой код попытки или пример вывода – Wain

ответ

4

Вы можете избежать грубой силы O(n^2) вложенной циклической (и перечисление) раствор сначала путем сортировки массива, а затем отфильтровать повторяющиеся цветовые объекты, используя, например, поиск значения хэш-функции (Метод 1 ниже) или умное исключение в отношении сортированной матрицы (Способ 2 ниже).

Отметим также класс типа именования (CamelCase): так Foo, а не foo.

Отказа от ответственности: Не смотрите сам слеп на асимптотической нотации сложности ниже, так как premature optimization есть, в зависимости от контекста и предполагаемой область применения вашей программы, вообще что-то грех. Я включил их ниже просто, чтобы иметь некоторую меру, чтобы сравнить различные методы. Выберите тот, который, по вашему мнению, имеет для вас наибольший смысл.


Метод 1

Наихудший ...

  • время сложность: O(n log n)

  • пространство сложность: O(n)

где пространство сложность относится к пространству, используемому в избытке массива , которому назначен конечный результат.

  • Пусть Foo соответствовать Hashable (пусть hashValue относятся к .color собственности).
  • Сортировка массива Foo экземпляров w.r.t. убывающий размер (.size свойство).
  • Отфильтровать отсортированную массив w.r.t. до первого появления каждого цвета, используя соответствие Hashable для быстрого использования O(1) поиск значения хеш для существующего цвета в словаре Foo:Bool. Адаптировано из комментариев Airspeed Velocity в the following answer.

Метод 2 (как это было предложено Николаем Ruhe):

худшем случае ...

  • время сложность: O(n log n)

  • пространство сложность: O(1)

  • Сортировка массива по цвету (первичного) и размер (вторичной).
  • Отфильтруйте отсортированный массив для элементов, которые имеют другой цвет, чем их предшественники.

Для третьего (вероятно, одним из лучших вариантов для этого приложения) метод, see Nikolai Ruhe:s answer below, представляющий метод с O(n)/O(n) времени/пространства худшем случае сложности, соответственно.


Реализации

[Этот шаг необходим только для метода 1] Соответствуют Foo к Hashable и Equatable:

/* Let Foo conform to Hashable */ 
class Foo : Hashable { 

    var color: String 
    var size: Int 
    var shape: String 

    init(color:String, size:Int, shape:String){ 
     self.color = color 
     self.size = size 
     self.shape = shape 
    } 

    var hashValue: Int { 
     return color.hashValue 
    } 

} 

/* And Equatable */ 
func ==(lhs: Foo, rhs: Foo) -> Bool { 
    return lhs.color == rhs.color 
} 

Настройка и пример для метода фильтра (ов), который следует вкратце:

/* Foo array example */ 
var array = [Foo]() 

array.append(Foo(color: "Blue", size: 2, shape: "Round")) 
array.append(Foo(color: "Red", size: 3, shape: "Square")) 
array.append(Foo(color: "Blue", size: 5, shape: "Round")) 
array.append(Foo(color: "Yellow", size: 1, shape: "Triangle")) 
array.append(Foo(color: "Blue", size: 1, shape: "Hexagon")) 

фильтр в соответствии с вашими требованиями:

/* Method 1 (assumes Foo conforms to Hashable (& Equatable)) */ 
var addedDict = [Foo:Bool]() 
var arrFiltered = array.sort{ $0.0.size > $0.1.size } 
    .filter {addedDict.updateValue(true, forKey: $0) == nil } 

/* Method 2 (as proposed by Nikolai Ruhe)      */ 
var previousColor: String? 
let arrFiltered = array.sort{ $0.color == $1.color ? $0.size > $1.size : $0.color < $1.color } 
    .filter{ if $0.color != previousColor { previousColor = $0.color; return true }; return false } 
    /* condensed .filter solution by @Nikolai Ruhe, thanks! */ 

Результат:

for bar in arrFiltered { 
    print(bar.color, bar.size) 
} 

/* Blue 5 
    Red 3 
    Yellow 1 */ 

Этап сортировки является доминирующим шагом в этом растворе (для обоих методов). От swift/stdlib/public/core/Sort.swift.gyb кажется, что Swift использует introsort (в частности, гибрид интросортирования в сочетании с insertion sort), работающий в худшем случае, как O(n log n).

+0

Почему бы не использовать 'Set' и' maxElement'? :) – Eendje

+0

@Eendje Я действительно играл с этой комбинацией, прежде чем прибегать к сортировке (не предназначен для каламбуров); Я не мог заставить прежний метод работать по назначению. Но если его можно использовать, это, возможно, еще более быстрое решение. Если вы можете исправить это, вы должны добавить его в качестве ответа! – dfri

+0

Я сделал, но его сомнительно, если это лучше. Я подумал, может быть, вы могли бы сделать это лучше: p Я отправлю его как ответ, чтобы показать вам :) – Eendje

1

Вы можете попробовать этот метод с использованием фильтра, но это может занять много времени, когда вы массив огромно, так как вы должны перебирать массив для каждого элемента.

let arrayFiltered = array.filter { (fooElement) -> Bool in 
    for (idx, fooItem) in array.enumerate() { 

     if fooItem.color == fooElement.color && fooItem.size > fooElement.size { 
      return false 
     } 
    } 
    return true 
} 
3
let result = Set(array).flatMap { color in array.filter { $0 == color }.maxElement { $0.0.size < $0.1.size } } 

Комбинация Set и maxElement.

Поскольку я использовал примеры ответа dfri, следует отметить, что объекты в array должны соответствовать Hashable и Equatable. Ответ сделан только для того, чтобы показать альтернативу, и лично я считаю, что ответ dfri намного лучше (и быстрее).

+0

Я собирался удалить этот «ответ» (я просто хотел показать dfri), но я не мог сказать, почему голос? – Eendje

+0

Возможно, это непонятно и громоздко? Мой 2ct. –

+0

Я думаю, что это правильный ответ (но не так, чтобы его нигде не затушевывали), даже если он немного сложный сам по себе. Eendje: возможно, следует прямо упомянуть, что этот ответ зависит от 'Foo', соответствующего« Hashable »и« Equatable »(в соответствии с предыдущими требованиями, соответствующими последним): возможно, кто-то пробовал вашу строку кода, не делая этого. – dfri

2

Вот простой и очень производительным решением, которое не нуждается в Hashable или каких-либо других модификаций на Foo:

var biggestFoos = [String: Foo]() 
for foo in array where biggestFoos[foo.color]?.size < foo.size { 
    biggestFoos[foo.color] = foo 
} 
let result = Array(biggestFoos.values) 
+0

На моей поездке на автобусе я понял, что решение слова [[String: Foo]] будет еще лучше; пришел, написал один (но не такой сжатый, как ваш 'for ... where' выше), и так же, как я ввожу этот поток, чтобы обновить свой ответ, я вижу, что вы уже опередили меня. Я считаю, что это решение «O (n)» (преждевременная оптимизация - это грех, но поскольку я уже перешел на ноту Big-O в моем решении ...) должен быть принятым ответом +1. – dfri

+0

@dfri Спасибо. Я думаю, что ваша идея сортировки исходного массива имеет некоторые преимущества и может быть улучшена до хорошего решения. Преимущество заключается в том, что он не требует дополнительного хранения. Таким образом, идеальным алгоритмом (если есть ограничение пространства) будет сортировка массива по цвету (первичный) и размер (вторичный), а затем выберите каждый элемент с другим цветом, чем его предшественник. Интересное упражнение (и вы получите мое преимущество) :) –

+0

Спасибо за отзыв и идею. И интересное упражнение действительно, однако, я думаю, что это получилось [немного беспорядочно] (https://gist.github.com/dfrib/847071ff64c856db8f1c), но, возможно, я поврежден swift.SO:s склонность к сжатым кодам , – dfri

Смежные вопросы