【问题标题】:Equalize two ULs by height按高度均衡两个 UL
【发布时间】:2011-10-18 08:38:18
【问题描述】:

我有一个案例,我获得了一个 UL,以及未知数量的 LI。我希望使用 JS 将 LI 拆分为 2 个 UL(我使用的是 jQuery)。我可以按 LI 的数量平均分割 UL,但我也想根据每个 LI 的高度来分割它,以便两个 UL 接近相同的高度。

对此的任何帮助将不胜感激,我觉得我开始使用的方法没有取得任何进展。

谢谢。

编辑:我目前拥有的 JS 代码。 HTML 只是一个直接的 UL/LI,每个 LI 可以有不同的高度。

var $sections = $('div.subsection');

$sections.each(function(){
  var $section = $(this);

  var $list = $section.children('ul');
  var $items = $list.children('li');
  var itemCount = $items.size();
  var leftover = itemCount % 2;
  var itemsPerColumn = Math.floor(itemCount / 2);
  var $newList = $('<ul />');

  $items.each(function(){
    var $this = $(this);
    var index = $items.index($this);

    if (index >= (itemsPerColumn + leftover)) {
      $this.remove().appendTo($newList);
    }
  });

  $list.after($newList);

  _equalizeListHeights();

  function _equalizeListHeights(){
    var listHeight = $list.height();
    var newListHeight = $newList.height();

    if (listHeight > newListHeight){
      var $lastItem = $list.children('li:last');
      var lastItemHeight = $lastItem.height();

      if (listHeight - lastItemHeight > newListHeight + lastItemHeight){
        $lastItem.remove().prependTo($newList);
        _equalizeListHeights();
      }
    }
  }

});

【问题讨论】:

  • 请提供 HTML 和 CSS,以便我们查看。 ;-)
  • 你的方法是什么??
  • 对不起,我附上了相关代码
  • 这可能更适合math.stackexchange:给定一组数字(高度),将它们分成两组,最小化|sum(A) - sum(B)|
  • @alex:您可以在 math.stackexchange 上简化您的问题:给定一组整数,找到一个子集,其总和尽可能接近整个集合总数的一半,但不超过所述总数(见我的回答)。

标签: javascript jquery


【解决方案1】:
$("ul.first li").each(function() {
    if ($("ul.first").outerHeight() > $("ul.second").outerHeight()) {
        $(this).appendTo($("ul.second"));
    }
});

使用这个 jQuery 代码。

【讨论】:

  • 这或多或少是我目前正在做的事情。但是,如果在所有的 LI 中,有一个比其他的高得多,那么这种方法就行不通(与另一种相比,任一 UL 都太高了)。所以我一直在寻找可以根据高度来安排 LI 的聪明方法。因此,如果一个 LI 与其他 3 个 LI 一样高,则具有这个最高 LI 的 UL 中的 LI 将少于另一个。这有意义吗?
  • 你不能让它们完全相等。
【解决方案2】:

你可以通过 CSS 来实现:

.double_column_list li {float: left; width: 50%;}

<ul class="double_column_list">
    <li>Awesome Stuff Awesome Stuff Awesome Stuff Awesome Stuff Awesome Stuff</li>
    <li>Awesome Stuff</li>
    <li>Awesome Stuff</li>
    <li>Awesome Stuff</li>
</ul>

要获得 3 列,请设置 width: 33.333%、4 列 width: 25% 等等。

当然,如果您不断将一个li 的高度增加到其他lis 无法匹配的程度,这看起来会很糟糕。但是,这个问题也不能通过 JS 来解决。

http://jsfiddle.net/rQJQb/

更新: 正如评论者所指出的,如果列表项未按高度排序(即中间任何一个列表项的高度可能大于/小于其前面的高度),则需要进行排序:http://jsfiddle.net/rQJQb/2/

【讨论】:

  • Doesn't always work as intended(为清楚起见添加了边框)。
  • @Eric:无论如何这都会是个问题,除非可以根据高度对列表项重新排序。需要根据高度对列表项进行预检。
  • 这不是我想要达到的目标。我遇到问题的不是多余的列,而是以这样的方式排列 LI,使两列都接近相同的高度。假设我有 10 个项目的原始 UL,如果每个 LI 的高度相同,我会将其拆分为 5/5。但是,如果一个 LI 比任何其他 LI 高 3 倍,那么我会将其拆分为 3/7。有意义吗?
  • 只要您的列表项按高度排序,CSS 就会这样做。如果没有,试试这个:jsfiddle.net/rQJQb/2
  • CSS 绝对无法解决我的问题...请仔细阅读我的问题...
【解决方案3】:

我想我至少可以看到这里的方法:

  1. 计算所有列表项的总高度 (total),并存储所有单独的高度
  2. 计算一个列表的高度 (total / 2)
  3. 确定一种算法,将一组heights 相加,尽可能地与total / 2 相加,但不超过它。
  4. 将具有这些高度的元素放入第一个列表中,将其余元素放入第二个列表中

第 3 步比较棘手。与Subset Sum Problem有关。


编辑

Here's a brute-force algorithm 可以解决您的问题。它不能在window.resize 上运行,因为那太傻了。如果您想看到它的变化,请调整结果窗口的大小,然后按运行。

//Sum a jQuery wrapped array
$.fn.sum = function() {
    var total = 0;
    this.each(function() { total += this; });
    return total;
};
//Mask elements with a bitmask
$.fn.mask = function(mask) {
    return this.filter(function(i) {
        return (mask >> i) & 1;
    })
}

//Get the sizes, and sneakily return a jQuery object
var sizes = $('.first li').map(function() { return $(this).outerHeight() });

var total = sizes.sum();
var maxTotal = total / 2;

var best = {
    total: 0,
    mask: 0
}

for (var subsetMask = 1; subsetMask < (1 << sizes.length); subsetMask++) {
    //Sum all the heights in this subset
    var subsetTotal = sizes.mask(subsetMask).sum();

    //New optimal solution?
    if (subsetTotal > best.total && subsetTotal <= maxTotal) {
        best = {
            total: subsetTotal,
            mask: subsetMask
        };
    }
}

$('.first li').mask(best.mask).appendTo('.second');

【讨论】:

  • 同意。我按照你的建议在 math.stackexchange 上问了这个问题,希望他们能在算法部分提供帮助。
  • 感谢子集子问题的链接。引导我找到这正是我正在寻找的算法类型:en.wikipedia.org/wiki/Partition_problem
  • @alex heyd:我就是从那里找到子集和问题的!
  • 未知号码。但实际上,它应该在 10ish max 左右(软最大值)
  • 是的。我遇到了“贪婪”解决方案(stackoverflow.com/questions/6597180/…),虽然不完美,但可能适合我的需要(如果我无法适当地重构 Cam 链接到的代码)
【解决方案4】:

您要解决的 CS 问题

您应该查看Backpack Problem。要“插入背包”的物品将是 LI。每个 LI 将具有与其高度相等的重量和值。背包容量应为所有 LI 高度之和的一半。运行一个算法来解决背包问题,您的 LI 将按照您所描述的高度分为两组。

直观的解释

背包算法找到一个项目子集,使得价值尽可能大,但重量不超过背包容量。但是我们每个LI的重量和价值就是它的高度,背包容量是所有LI总高度的一半。

所以本质上它会给我们的是一组所有 LI,使得高度尽可能高而不超过总高度的 1/2。如果您最终应该得到两组等高的 LI,这将是其中一组。如果您最终应该得到两组具有不同高度的 LI,则背包问题解决方案将返回具有较小高度的集合(其余未选择的 LI 将是第二组)。

解决方案

试试这里使用的代码:http://rosettacode.org/wiki/Knapsack_problem/Bounded#JavaScript

或者如果你想自己动手(不推荐——何必呢?):http://en.wikipedia.org/wiki/Knapsack_problem#Unbounded_knapsack_problem

【讨论】:

  • 谢谢,很有帮助。我需要重构您发布的链接中的代码,但它似乎可以处理我的情况
  • @alex:np。根据您项目的性质,请注意该代码的许可问题(检查 Rosetta 代码许可信息)。否则,我相信您可以在 JS 中找到另一种实现,或者在另一种语言中找到最坏的情况,您可以将其移植过来。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-04-15
  • 2011-06-15
  • 1970-01-01
  • 2015-02-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多