【问题标题】:Complex Sort for Array of Objects对象数组的复杂排序
【发布时间】:2016-01-07 20:50:34
【问题描述】:

所以我已经从事这项任务一段时间了,但我遇到了一个问题。我一遍又一遍地尝试和重写,似乎无法提出足够好的解决方案。

我正在尝试将一个对象数组排序到另一个对象数组中。我正在使用敲除,但如果可以在 vanilla JS 或 jQuery 中更直接地完成,则解决方案不必包含敲除。

我会解释:

我们从 observableArray serials 开始,其中包含一些序列号对象。这个数组总是包含这种格式:

var serials = ko.observableArray([
    { number: "DFG09008", qty: 1 }, 
    { number: "DFG09009", qty: 1 },
    { number: "DFG09010", qty: 1 },
    { number: "DFG09011", qty: 1 },
    { number: "DFG09020", qty: 1 },
    { number: "ASD-0001", qty: 1 },
    { number: "ASD-0002", qty: 1 },
    { number: "HJ-DRT-06", qty: 1 },
    { number: "HJ-DRT-07", qty: 1 },
    { number: "POU055054", qty: 1 }
]);
// qty property is for something unrelated to this in the app

我正在编写的函数的预期结果是,它将输出到另一个 observableArray displaySerials,用于呈现“数值范围”的概念:

var displaySerials = ko.observableArray([
    { beginSerial: "DFG09008", endSerial: "DFG09011" },
    { beginSerial: "DFG09020", endSerial: "" },
    { beginSerial: "ASD-0001", endSerial: "ASD-0002" },
    { beginSerial: "HJ-DRT-06", endSerial: "HJ-DRT-07" },
    { beginSerial: "POU055054", endSerial: "" }
]);

所以,因为serials'前四个成员'number属性,"DFG09008", "DFG09009", "DFG09010", "DFG09011"有一个匹配的前缀(DFG090)和连续的后缀数字(8, 9, 10, 11),它们被放入一个对象中beginSerial 是范围内的最小数字 (DFG09008),endSerial 是最大数字 (DFG09011)。请注意,"DFG09020" 被放在一个单独的对象中。这是因为它与组中的其他数字不连续,因此它被单独考虑。

任何与组的其余部分无关的序列号都放入它自己的对象中,其中endSerial 属性是一个空字符串:{ beginSerial: "DFG09020", endSerial: "" }

序列号的唯一定义规则是它们总是以数字结尾。但集合中可能有数十或数百个。

有效解决方案的唯一要求是上述内容以及:

  • 数组serials 在此功能过程中应保持不变。
  • 解决方案不是特定的,也不依赖于连续剧的任何字符串结构,除了它们以数字结尾。

My jsfiddle

非常感谢任何贡献!

【问题讨论】:

  • 您尚未在问题本身中包含当前排序的方式。您能否添加它,并告诉我们具体问题出在哪里? (此外,您将从单元测试中很多受益。将您的人类可读规范转换为自动化测试:这肯定值得您花时间!)

标签: javascript jquery arrays knockout.js


【解决方案1】:

看起来您不需要对它们进行排序,而是对它们进行分组。它们似乎已经井然有序。您需要做的就是浏览列表,看看每一个是否与前一个连续。如果是,它就是你正在构建的 displaySerial 的结尾;如果没有,则启动一个新的 displaySerial。

也许棘手的部分是确定它们是否是连续的。我选择使用正则表达式来拆分尾随数字。如果非数字部分相等而一个数字不同,则它是连续的。

这里不需要 Knockout 或 jQuery。你没有对 DOM 做任何事情。您可以使用实用程序函数,但没有太多理由这样做。我使用 Knockout 只是为了显示结果。

如果您确实需要对它们进行排序,您可以使用具有相同正则表达式拆分器的比较函数,首先比较非数字部分,如果非数字相等,则比较数字。

// test data
var serials = [
    { number: "DFG09008", qty: 1 }, 
    { number: "DFG09009", qty: 1 },
    { number: "DFG09010", qty: 1 },
    { number: "DFG09011", qty: 1 },
    { number: "DFG09020", qty: 1 },
    { number: "ASD-0001", qty: 1 },
    { number: "ASD-0002", qty: 1 },
    { number: "HJ-DRT-06", qty: 1 },
    { number: "HJ-DRT-07", qty: 1 },
    { number: "POU055054", qty: 1 }
];


// constructor for displaySerials array
function DisplaySerial (beginSerial, endSerial) {
    this.beginSerial = beginSerial;
    this.endSerial = endSerial;
}

var serialPartsPattern = /(.*\D)(\d+)$/;
function isSequential(s1, s2) {
    if (s1 === undefined) return false;
    var m1 = s1.match(serialPartsPattern),
        m2 = s2.match(serialPartsPattern);
    return m1[1] === m2[1] && (+m1[2] + 1) === +m2[2]; 
}

var displaySerials = [],
    currentSerial,
    prevSerial,
    currentDisplaySerial;
for (var i=0; i<serials.length; ++i) {
    currentSerial = serials[i].number;
    if (isSequential(prevSerial, currentSerial)) {
        currentDisplaySerial.endSerial = currentSerial;
    }
    else {
        currentDisplaySerial = new DisplaySerial(currentSerial, "");
        displaySerials.push(currentDisplaySerial);
    }
    prevSerial = currentSerial;
}

ko.applyBindings({displaySerials: displaySerials});
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<ol data-bind="foreach:displaySerials">
  <li><span data-bind="text:beginSerial"></span>...<span data-bind="text:endSerial"></span></li>
  </ol>

【讨论】:

    【解决方案2】:

    我附上了一个基于lodash 的解决方案。我假设您的值不一定是排序的。请注意,我添加了更多值以正确显示它的排序和分组,并且我更改了顺序。

    var serials = [
        { number: "DFG09008", qty: 1 }, 
        { number: "DFG09009", qty: 1 },
        { number: "DFG09016", qty: 1 },
        { number: "DFG09010", qty: 1 },
        { number: "DFG09011", qty: 1 },
        { number: "DFG09005", qty: 1 }, 
        { number: "DFG09014", qty: 1 },
        { number: "DFG09015", qty: 1 },
        { number: "DFG09020", qty: 1 },
        { number: "ASD-0001", qty: 1 },
        { number: "ASD-0002", qty: 1 },
        { number: "HJ-DRT-06", qty: 1 },
        { number: "HJ-DRT-07", qty: 1 },
        { number: "POU055054", qty: 1 }
    ];
    
    var splitSerials = _.map(serials, function(serial){
      var parts = serial.number.match(/([^\d]+)(\d+)/);
      return {key: parts[1], number: parts[2]};
    });
    
    var grouped = _.groupBy(splitSerials, 'key');
    
    function chunkConsec(array){
      return _.transform(array, function(result, n){
        if(_.last(_.last(result)) === n - 1){
          _.last(result).push(n);
        } else {
          result.push([n]);
        }
        return true;
      });
    }
    
    function unchunk(array){
      if(array.length > 1){
        return [_.first(array), _.last(array)];
      } else {
        return array;
      }
    }
    
    var groupedArray = _.mapValues(grouped,
      _.flow(
        _.partial(_.map, _, 'number'),
        _.partial(_.map, _, _.parseInt),
        _.sortBy,
        chunkConsec,
        _.partial(_.map, _, unchunk)
      ));
    
    function buildRange(value, key){
      var result = {beginSerial: (key + value[0])};
      result.endSerial = (value.length > 1 ? key + value[1] : '');
      return result;
    }
    function buildRanges(values, key){
      return _.map(values, function(value){
        return buildRange(value, key);
      });
    }
    
    var finalResult = _.flatten(_.values(_.mapValues(groupedArray, buildRanges)));
    console.log(finalResult);
    alert(JSON.stringify(finalResult));
    &lt;script src="https://cdn.rawgit.com/lodash/lodash/3.0.1/lodash.min.js"&gt;&lt;/script&gt;

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-11-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-21
      相关资源
      最近更新 更多