我有一个哈希列表,如下:
incoming_links = [
{:title => 'blah1', :url => "http://blah.com/post/1"},
{:title => 'blah2', :url => "http://blah.com/post/2"},
{:title => 'blah3', :url => "http://blah.com/post/3"}]
一个ActiveRecord模型,其中包含一些匹配行的数据库中的字段,例如:
Link.all =>
[<Link#2 @title='blah2' @url='...post/2'>,
<Link#3 @title='blah3' @url='...post/3'>,
<Link#4 @title='blah4' @url='...post/4'>]
我想在Link.all
上使用incoming_links
进行设置操作,以便我可以发现<Link#4 ...>
不在incoming_links
和{{{ 1}}不在{:title => 'blah1', :url =>'http://blah.com/post/1'}
集合中,如此:
Link.all
糟糕的解决方案a):
我宁愿不重写#pseudocode
#incoming_links = as above
links = Link.all
expired_links = links - incoming_links
missing_links = incoming_links - links
expired_links.destroy
missing_links.each{|link| Link.create(link)}
等等,我也可以将Array#-
转换为一组未保存的incoming_links
个对象;所以我尝试在Link
中覆盖hash
eql?
等等,以便忽略Link
默认提供的id
等式。但这是在应用程序中应该考虑这种平等的唯一地方 - 在其他地方需要Link#id默认标识。有没有什么方法可以将Link子类化并应用AR::Base
,hash
等覆盖那里?
糟糕的解决方案b):
我尝试过的另一条路线是为每个链接提取属性哈希并执行eql?
以修剪哈希值。但这需要编写单独的.slice('id',...etc)
方法,以便在对哈希进行集合操作时跟踪链接对象,并编写单独的代理类来包装-
哈希和链接,这看起来有点矫枉过正。尽管如此,对我来说这是current solution。
您能想到设计此互动的更好方法吗?额外的清洁信用。
答案 0 :(得分:1)
试试这个
incoming_links = [
{:title => 'blah1', :url => "http://blah.com/post/1"},
{:title => 'blah2', :url => "http://blah.com/post/2"},
{:title => 'blah3', :url => "http://blah.com/post/3"}]
ar_links = Link.all(:select => 'title, url').map(&:attributes)
# wich incoming links are not in ar_links
incoming_links - ar_links
# and vice versa
ar_links - incoming_links
<强> UPD 强>
对于您的链接模型:
def self.not_in_array(array)
keys = array.first.keys
all.reject do |item|
hash = {}
keys.each { |k| hash[k] = item.send(k) }
array.include? hash
end
end
def self.not_in_class(array)
keys = array.first.keys
class_array = []
all.each do |item|
hash = {}
keys.each { |k| hash[k] = item.send(k) }
class_array << hash
end
array - class_array
end
ar = [{:title => 'blah1', :url => 'http://blah.com/ddd'}]
Link.not_in_array ar
#=> all links from Link model which not in `ar`
Link.not_in_class ar
#=> all links from `ar` which not in your Link model
答案 1 :(得分:0)
如果你重写了相等方法,ActiveRecord会抱怨吗?
你不能做类似的事情(比如常规的ruby类):
class Link
attr_reader :title, :url
def initialize(title, url)
@title = title
@url = url
end
def eql?(another_link)
self.title == another_link.title and self.url == another_link.url
end
def hash
title.hash * url.hash
end
end
aa = [Link.new('a', 'url1'), Link.new('b', 'url2')]
bb = [Link.new('a', 'url1'), Link.new('d', 'url4')]
(aa - bb).each{|x| puts x.title}
答案 2 :(得分:0)
要求是:
# Keep track of original link objects when
# comparing against a set of incomplete `attributes` hashes.
# Don't alter the `hash` and `eql?` methods of Link permanently,
# or globally, throughout the application.
当前解决方案使用Hash的eql?
方法生效,并使用原始对象注释哈希:
class LinkComp < Hash
LINK_COLS = [:title, :url]
attr_accessor :link
def self.[](args)
if args.first.is_a?(Link) #not necessary for the algorithm,
#but nice for finding typos and logic errors
links = args.collect do |lnk|
lk = super(lnk.attributes.slice(*(LINK_COLS.collect(&:to_s)).to_a)
lk.link = lnk
lk
end
elsif args.blank?
[]
#else #raise error for finding typos
end
end
end
incoming_links = [
{:title => 'blah1', :url => "http://blah.com/post/1"},
{:title => 'blah2', :url => "http://blah.com/post/2"},
{:title => 'blah3', :url => "http://blah.com/post/3"}]
#Link.all =>
#[<Link#2 @title='blah2' @url='...post/2'>,
# <Link#3 @title='blah3' @url='...post/3'>,
# <Link#4 @title='blah4' @url='...post/4'>]
incoming_links= LinkComp[incoming_links.collect{|i| Link.new(i)}]
links = LinkComp[Link.all] #As per fl00r's suggestion
#this could be :select'd down somewhat, w.l.o.g.
missing_links = (incoming_links - links).collect(&:link)
expired_links = (links - incoming_links).collect(&:link)