Почему некоторые элементы остаются, даже если условие соответствует каждому? - PullRequest
3 голосов
/ 28 мая 2019

Я создал небольшое приложение для магазина в Ruby.Концепция проста: я хочу иметь возможность добавлять, удалять и просматривать «базу данных» в этом примере.Код, кажется, работает;однако, он не ведет себя так, как можно было бы ожидать.Я могу добавить любой товар просто отлично, но когда я пытаюсь удалить его по определенному критерию, который соответствует каждому элементу, удаляются только некоторые из них.

class Store
  PRODUCT = Struct.new(:identifier, :parameters)

  def initialize(database = [])
    @database = database
  end

  def increment
    # Increment the unique identifier if the database is not empty
    # Otherwise, set the initial value for the unique identifier
    @database.any? ? @database.map { |object| object[:identifier] }.max + 1 : 1
  end

  def add(product)
    @database.push(identifier: increment, parameters: product)
  end

  def remove(product)
    @database.each do |object|
      product.each do |key, value|
        @database.delete(object) if object[key] == value
        @database.delete(object) if object[:parameters][key] == value
      end
    end
  end

  def view
    puts @database
    puts "\n"
  end
end

store = Store.new

store.add(name: 'Fanta', volume: 33, unit: 'centiliter', count: 48, price: 9.95, currency: 'SEK')
store.add(name: 'Sprite', volume: 33, unit: 'centiliter', count: 48, price: 9.95, currency: 'SEK')
store.add(name: 'Coca-Cola', volume: 33, unit: 'centiliter', count: 48, price: 9.95, currency: 'SEK')

store.view

store.remove(price: 9.95)

store.view

В этом примере будут добавлены три произвольных элемента к Storeэкземпляр и список содержимого, найденного в массиве @database.Этот массив, конечно, будет содержать значения:

{:identifier=>1, :parameters=>{:name=>"Fanta", :volume=>33, :unit=>"centiliter", :count=>48, :price=>9.95, :currency=>"SEK"}}
{:identifier=>2, :parameters=>{:name=>"Sprite", :volume=>33, :unit=>"centiliter", :count=>48, :price=>9.95, :currency=>"SEK"}}
{:identifier=>3, :parameters=>{:name=>"Coca-Cola", :volume=>33, :unit=>"centiliter", :count=>48, :price=>9.95, :currency=>"SEK"}}

Затем код переходит к удалению любого продукта по цене 9,95, которая должна соответствовать всем продуктам в массиве @database.Однако, когда я перечисляю его содержимое еще раз, остается один продукт:

{:identifier=>2, :parameters=>{:name=>"Sprite", :volume=>33, :unit=>"centiliter", :count=>48, :price=>9.95, :currency=>"SEK"}}

Я не могу найти никакой логики для этого.Почему мой метод remove не удаляет все элементы, если условие выполняется для каждого сравнения?

Ответы [ 2 ]

2 голосов
/ 28 мая 2019

Давайте сосредоточимся на следующем цикле,

@database.each do |object|
  product.each do |key, value|
    puts '-----------------'
    @database.delete(object) if object[key] == value
    @database.delete(object) if object[:parameters][key] == value
  end
end

Предполагается запустить 3 итерации в каждом цикле для @database,

первая итерация - удалитьпервый с идентификатором = 1 для каждого счетчика цикла 0

вторая итерация - он изменяется @database при удалении первого, второй становится первым и 3-й становится вторым, а счетчик каждого цикла имеет значение1. Таким образом, он удаляет новый 2-й элемент @database с идентификатором = 3.

третья итерация - у него будет каждый счетчик цикла в 2, а у нового обновленного @database будет только 1элемент с идентификатором = 2 (спасен во 2-й итерации), и нет третьего элемента для продолжения итерации, поэтому ничего не повторяется.

Требуется коррекция:

def remove(product)
  @database.reject! do |object|
    product.map do |k,v|
      object[k] == v if object.has_key?(k)
      object[:parameters][k] == v if object[:parameters].has_key?(k)
    end.inject(&:|)
  end
end
0 голосов
/ 28 мая 2019

Я думаю, что эта проблема возникает из сравнения с плавающей точкой.Есть много способов приблизиться к этому.Один из них:

  def remove(product)
    @database.delete_if do |object|
      product.any? do |key, value|
        matches?(object[key], value) || matches?(object[:parameters][key], value)
      end
    end
  end

  def matches?(obj1, obj2)
    if obj1.is_a?(Float) || obj2.is_a?(Float)
      (obj1-obj2).abs < 0.001
    else
      obj1 == obj2
    end
  end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...