2010-04-30 42 views
1

我有散列的列表清單,如:比較現場哈希有相當的AR-對象

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.allincoming_links,這樣我可以弄清楚,<Link#4 ...>不在一套incoming_links,並{:title => 'blah1', :url =>'http://blah.com/post/1'}不在Link.all集,像這樣:

#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對象;所以我試圖在Link中覆蓋hasheql?等等,以便忽略AR::Base默認提供的id相等性。但這是唯一應該在應用程序中考慮這種平等的地方 - 在其他地方,Link#id缺省標識是必需的。有什麼方法可以繼承Link並應用hasheql?等改寫?

蹩腳的解決方案B):

我已經試過另一條路線是拔出來爲每個鏈接的屬性散列並做了.slice('id',...etc)修剪下來的哈希值。但是這需要編寫獨立的-方法來跟蹤鏈接對象,同時對哈希進行設置操作,編寫單獨的代理類來包裝incoming_links哈希和鏈接,這似乎有點矯枉過正。儘管如此,這是我的current solution

你能想出一個更好的方法來設計這種互動嗎?清潔度額外信貸。

回答

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) 
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 
+0

其中一個關鍵點是保持e鏈接周圍的對象,並知道哪些已被排除。要用你的':select'建議來做到這一點,我必須做第二件事 - 寫一個類或方法,在保留Link對象id的同時進行減法。 雖然我只需加載所需的字段是一個有趣的想法,因爲當我需要加速整個過程。 – 2010-05-01 02:14:38

+0

我已經更新了我的答案 – fl00r 2010-05-01 11:41:23

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} 
+0

你認爲這可以作爲Link的子類嗎?那樣我可以改變eql?和散列,但不是全局。我害怕ActiveRecord關聯內部,以及他們用'hash'和'eql''方法做了什麼,但如果這是一個子類(稱之爲LinkComp),我可能會成功(LinkComp)爲所有'鏈接'和'incoming_links.collect {| il | LinkComp.new(LI)}'。我可以試試這個。 – 2010-05-03 18:53:08