如何有效地将稀疏数组重新排序为新的非稀疏数组

时间:2012-08-16 15:12:46

标签: javascript

[我为问题的复杂性提前道歉......但是什么好问题很简单?]

我管理一个大型(22名成员)生产支持团队的待命名单。该列表是“完全升级”(列出所有团队成员)并每月生成。由于靠近列表顶部的那些人被称为隔夜问题(并且往往不可用),我们反过来利用该列表来创建我们的日间分配名单。

问题

在一段不合理的时间之后,政治和论证(不要问)制定了一个相当愚蠢的规则集,并同意产生这个名单。要生成每日作业名单:

“向后退出通话清单,在列表中选择”偶数“排名,然后按降序排列第一位。然后对”将赔率“放在名单上做同样的事情。”

所以,一个简单的例子:

随叫随到:“1-Jack,2-Jim,3-Jane,4-John,5-Jill,6-Joe” 名单:“1-Joe,2-John,3-Jim,4-Jill,3-Jane,1-Jack”

主要的皱纹是,由于休假,PTO,其他任务等时间,待命列表稀疏(可能有空位)。因此,一个更现实世界的例子可能是:

随叫随到:“1-Jack,3-Jane,4-John,6-Joe” 名单:“1-Joe,2-John,3-Jane,4-Jack

真正的名单是22人。在任何给定的一天,我们平均约17或18可用。失踪的人不会影响随叫随到 - 你只是继续前进到下一个最高点 - 但是他们正在让名册规则中的工作变得痛苦。

当前(Inelegant)解决方案

目前我有这种蛮力风格。我首先创建一个表示on-call的对象数组,其中每个对象都有一个人的名字和待命等级。 (我刚想到我可以通过创建一个稀疏数组来简化这一点,这些数组只是索引代表实际排名的名称......但我认为它不会改变这个问题。)

然后我从最后到第一个循环遍历数组:首先收集偶数等级(通过获得Rank的模数)并将它们推到一个新数组上,然后再收集几率:

                       // Get the Current Oncall 
           var Oncall = new Array(); 
           for ( var iCnt = 1; iCnt <= 22; iCnt++ ) { 
                   var CurOncall = DataRows[Cnt].getAttribute("ows_OnCall" + iCnt); 
                   if ( CurOncall != null ) { 
                           Oncall[Oncall.length] =  {"Name":CurOncall, "Rank": iCnt}; 
                   }; 
           }; 
                   // Get the Current Roster 
           var Roster = new Array(); 
                   // Add the "evens" 
           for ( var iCnt = Oncall.length - 1; iCnt >= 0; iCnt-- ) { 
                           // Get the Current Incident Rank 
                   if ( Oncall[iCnt].Rank % 2 == 0 ) { 
                           Roster[Roster.length] = Oncall[iCnt].Name; 
                   }; 
           } 
                   // Add the "odds" 
           for ( var iCnt = Oncall.length - 1; iCnt >= 0; iCnt-- ) { 
                           // Get the Current Incident Rank 
                   if ( Oncall[iCnt].Rank % 2 != 0 ) { 
                           Roster[Roster.length] = Oncall[iCnt].Name; 
                   }; 
           } 

请注意,此代码段存在于更大的循环中(我循环了一周的数据,这只是一天)。 DataRows [Cnt] 是从SharePoint网络服务中提取的当天信息。

同样,这种方法很好,但是对于每天的处理,需要在同一数据上进行三次循环。

当前(破碎)解决方案

我想做的就是进入poing,我可以使用单个循环从通话中生成名册。直接进入,我一直在努力将后两个循环合二为一。假设Oncall数组生成与上面相同,这是我目前的尝试(它有点难看):

       var IncCnt = 1; 
   for ( var Cnt = OnCall.length - 1; Cnt >= 0; Cnt-- ) { 

                   // Get the Current Incident (Roster) Rank 
           if ( OnCall[Cnt].Rank % 2 == 0 ) { 
                   CurIncRank = Math.ceil(IncCnt / 2); 
           } else { 
                   CurIncRank = Math.ceil(IncCnt / 2) + Math.floor(OnCall.length / 2) 
           }; 

           Roster[CurIncRank] = OnCall[Cnt].Name;
                   // Increase the Incident Cnt
           IncCnt = IncCnt + 1; 

   }; 

这接近于工作,但往往要么重叠(用最后的“偶数”覆盖第一个“奇数”),要么根据稀疏度和元素总数在均匀和赔率之间留一个间隙。

结论

主要目标是直接在第一个循环中“动态”生成名单,而不是创建一个特定的随叫随到阵列然后从中生成 - 但目前我很乐意接受第二个snippt到在所有情况下工作。

我也很乐意这可能无法工作。也许,不优雅的规则集和不优雅的数据的组合只需要蛮力方法。如果是这种情况,我宁愿在放弃之前从比我更好的程序员那里听到它。

提前致谢。随意请求任何澄清。

2 个答案:

答案 0 :(得分:2)

所以,如果我正确地读了你,你有一个'onCall'对象数组,每个对象都包含一个名字和等级,如下所示:

var onCall = [
    {
        rank: 1,
        name: 'Jack'
    },
    {
        rank: 3,
        name: 'Jill'
    },
    ...
];

然后,您要创建一个名单数组,其中按降序排列均匀排名的人,然后按降序排列奇数排名的人。如果这是正确的,那么以下代码将生成这样的数组:

for(var i = onCall.length-1; i >= 0; i--) {
    person = onCall[i];
    if(person.rank % 2 === 0) {
        evens.push(person);
    } else {
        odds.push(person);
    }
}
roster = evens.concat(odds);

你反过来一次完成数组。对于每个人,根据他们的等级将他们添加到'平均'或'赔率'。最后,您只需将两个数组连接成一个新的“名册”数组。

这是一个演示:

--- jsFiddle DEMO ---

我很抱歉这不是用你的特定变量名写的,但是如果这是你正在寻找的,那么它应该很容易改变以适应你的环境。

答案 1 :(得分:0)

这是coffeescript中非常相似的答案(:

# List of all employees
employees = [
    { name: "Jack", onCall: false, Rank: 1 }
    { name: "Jim", onCall: true, Rank: 2 }
    { name: "Jane", onCall: false, Rank: 3 }
    { name: "John", onCall: false, Rank: 4 }
    { name: "Jill", onCall: true, Rank: 5 }
    { name: "Joe", onCall: false, Rank: 6 }
]

# Returns an array of eligible Employees in the order which they should be called
generateRoster = (employees) ->
    a = []
    b = []

    employees
        .reverse()
        .filter (employee) ->
            not employee.onCall
        .forEach (employee) ->
            if employee % 2 
                a.push(employee) 
            else 
                b.push(employee)
            return

    a.concat(b)

# Output the roster
console.log generateRoster employees