这是一个开放式的问题,并不是特定于语言的,尽管我最终写的代码将是Ruby / Rails。
我的应用中有一个页面,其中包含一组卡片,每张卡片都有一些内容。每张卡都有一个排名属性,它是一个正整数。我正在通过单击卡上的上/下按钮让用户重新排序卡片,这需要调整等级。例如,如果我想要移动#2,我需要用#1交换位置(如果有#1)。如果我想向下移动#1,我需要将地点与集合中的最后一张卡交换。
我遇到的问题是我有很多不良数据需要解决。我的大部分卡片都没有排名(rank = nil
)。此外,个人资料中的某些卡片不会按顺序排列。排名可能是[1,3,7,99,nil]
而不是[1,2,3,4,5]
。列表末尾会显示nil
等级的卡片。
如果数据是完美的,编写一个调整卡片等级的功能将非常容易,但事实并非如此。我想知道的是,如果有任何好的资源可以解决这个问题。我想确保我考虑所有可能的情况,例如当我将rank = 4
向上移动一张卡时该怎么办,但上一个插槽中的卡也有rank = 4
作为一项额外的挑战,此功能将在控制器中进行,因此我正在寻找一种降低复杂性的方法,以便在控制器方法中不会有100多行代码。
回答工作代码
如果它对其他人有帮助,这里是我提出的用于对不同类别的卡进行排名的工作代码:
def adjust_rank
company = current_user.company
# Build array of cards
cards = []
cards << company.office_card if company.office_card.present?
cards << company.twitter_card if company.twitter_card.present?
company.gallery_cards.each { |gc| cards << gc }
company.quote_cards.each { |qc| cards << qc }
company.wild_cards.each { |wc| cards << wc }
# Ensure no cards have nil for rank before sorting
cards.each_with_index { |c,i| c.rank = 100+i if c.rank.nil? }
cards.sort_by! { |x| x.rank }
# Ensure cards are ranked sequentially
cards.each_with_index { |c, i| c.rank = i }
# Save all cards in a single transaction. This works even though not all the cards belong to the same class.
cards[0].class.transaction do
cards.each { |c| c.save }
end
# This is the card whose rank the user is adjusting
selected = Object.const_get(rank_params[:type]).find(rank_params[:id])
# Get the adjacent card from the cards array
if rank_params[:direction] == 'up'
swap = cards.select { |c| c.rank == selected.rank + 1 }[0]
# If there is no card after selected, get first card in array
swap = cards[0] if swap.nil?
elsif rank_params[:direction] == 'down'
swap = cards.select { |c| c.rank == selected.rank - 1 }[0]
# If there is no card before selected, get last card in array
swap = cards[-1] if swap.nil?
end
# Swap ranks
swap_new_rank = selected.rank
selected_new_rank = swap.rank
swap.rank = swap_new_rank
selected.rank = selected_new_rank
respond_to do |format|
if swap.save && selected.save
format.json { render json: {}, status: :ok }
else
format.json { render json: {}, status: :bad_request }
end
end
end
答案 0 :(得分:1)
我做了类似排名内容的事情。我在Rails中构建了一个CMS,让用户可以订购显示内容的位置。
为了提供一个更加平台无关的例子,我将使用一些伪代码。我还假设您将使用某种类似ORM的ActiveMcord。
无论如何,我的解决方案是采用混合Javascript / Backend方法。我首先创建了我的Card
模型。迁移可能看起来像这样 -
create_table :cards do |t|
t.string :name
t.integer :position, :default => 0
end
请注意我有一个职位。对于id
来说,这似乎是多余的,但我将使用它来存储对象的可视位置。另请注意,我已经为position
提供了默认值,因此我们永远不会有如上所述的错误数据。
所以,让我们假设我们有以下种子数据 -
Card.create(name: 'card1', position: 1)
Card.create(name: 'card2', position: nil)
Card.create(name: 'card3', position: 6)
Card.create(name: 'card4', position: 5)
请注意,即使我们的卡片按顺序标记,它们的位置也可以自由改变。如果我们要Card.all
并根据position
对其进行排序,我们会得到一个如下所示的列表,假设我们输出了他们的名字。
card2 # position 0 since our default is 0
card1 # position 1
card4 # position 5
card3 # position 6
现在,我们希望能够重新排序我们的卡片。由于创建前端有很多解决方案,我将更抽象地解释这一部分。
在这个例子中,我假设每张卡都在它自己的div
元素内,并且在div
元素内是一个确定position
的隐藏形式。
基本上,当您单击箭头以在卡片组中向上或向下移动项目时,您需要重新评估其位置。代码可能如下所示:
function whenArrowClicked(){
for every card div{
this.form.val(i)
}
}
所以,我们的初步定位是
card2 # position 0
card1 # position 1
card4 # position 5
card3 # position 6
看起来更像
card2 # position 1
card1 # position 2
card4 # position 3
card3 # position 4
这更有意义!
此外,如果我们要执行类似移动card1
某个位置的操作,我们的javascript会修复我们的定位,因此position
值会反映视觉值。
+ card1 # position 1 - used to be 2
- card2 # position 2 - used to be 1
card4 # position 3
card3 # position 4
保存此表单将更新我们的对象并修复任何具有nil
位置的对象。
请记住,这是一个非常简短的解决方案,实际实施需要考虑更多细节 - 例如,如果您计划拥有大量的卡片,Card.all
将不是一种有效的方式抓住内容。此外,您可能会遇到尝试一次保存多个模型的问题。 This might help(见评论)。但是,希望这可以帮助您了解如何构建应用程序。