我正在尝试按属性值对对象集合进行排名。基本上每个玩家对象都有一个得分属性,应该从最高到最低排名。例如:
{
player1: {
nickname: "Bob",
score: 100,
rank: 4
},
player2: {
nickname: "Amy",
score: 200,
rank: 3
},
player3: {
nickname: "Grant",
score: 300,
rank: 2
},
player4: {
nickname: "Steve",
score: 200,
rank: 3
},
player5: {
nickname: "Joe",
score: 500,
rank: 1
}
}
按分数对这些对象进行排名应该会产生以下结果:
var array = [];
for (var key in players) {
array.push(players[key]);
}
array.sort(function(a, b){
return a.score - b.score;
});
for (var i = 0; i < array.length; i++) {
array[i].rank = i + 1;
}
重要的是,具有相同分数的对象应具有相同的等级。
据我所知:
Option Explicit
Public StopLoop As Boolean
Sub GetHtmlTable()
StopLoop = False
Do Until StopLoop = True
DoEvents
Dim objWeb As QueryTable
Sheets(1).Columns(1).ClearContents
With Sheets("Sheet1")
Set objWeb = .QueryTables.Add( _
Connection:="URL;http://www.spotternetwork.org/feeds/gr.txt", _
Destination:=.Range("A1"))
With objWeb
.WebSelectionType = xlSpecifiedTables
.WebTables = "1" ' Identify your HTML Table here
.Refresh BackgroundQuery:=False
.SaveData = True
End With
End With
Set objWeb = Nothing
'End Import of Text From http://www.spotternetwork.org/feeds/gr.txt==================
'Start Filter Out Unused Data========================================================
Dim i As Long
Dim j As Long
Dim LRow As Long
Dim LListRow As Long
Dim BMatch As Boolean
'Find last instance of "End:" in
LRow = Sheets(1).Range("A:A").Find(what:="End*", searchdirection:=xlPrevious).Row
'Find last non-blank row in column A of second sheet
LListRow = Sheets(2).Range("A:A").Find(what:="*", searchdirection:=xlPrevious).Row
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Application.DisplayAlerts = False
Application.EnableEvents = False
If LRow >= 11 Then
'Make sure there are at least 11 rows of data
i = LRow
'MsgBox "First checkpoint: Last row of data is " & LRow 'Comment out this line
Do
BMatch = False
For j = 1 To LListRow
'Test this block to see if the value from j appears in the second row of data
If InStr(1, Sheets(1).Range("A" & i - 2).Value2, Sheets(2).Range("A" & j).Value2) > 0 Then
BMatch = True
Exit For
End If
Next j
'Application.StatusBar = "Match status for row " & i & ": " & BMatch
If Not BMatch Then
'Loop backwards to find the starting row (no lower than 11)
For j = i To 11 Step -1
If Sheets(1).Range("A" & j).Value2 Like "Object:*" Then Exit For
Next j
Sheets(1).Rows(j & ":" & i).Delete
i = j - 1
Else
'Find next block
If i > 11 Then
For j = i - 1 To 11 Step -1
If Sheets(1).Range("A" & j).Value2 Like "End:*" Then Exit For
Next j
i = j
Else
i = 10 'Force the loop to exit
End If
End If
'Application.StatusBar = "Moving to row " & i
Loop Until i < 11
'Loop back through and delete any blank rows
LRow = Sheets(1).Range("A:A").Find(what:="*", searchdirection:=xlPrevious).Row
'MsgBox "Second checkpoint: new last row of data is " & LRow
For i = LRow To 11 Step -1
If Sheets(1).Range("A" & i).Value2 = vbNullString Then Sheets(1).Rows(i).Delete
Next i
End If
'Application.StatusBar = False
Application.EnableEvents = True
Application.DisplayAlerts = True
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
'End Filter Out Unused Data========================================================
'Start Write To Local Txt File=====================================================
Dim sSaveAsFilePath As String
Application.DisplayAlerts = False
sSaveAsFilePath = "C:\Users\Speedy\Desktop\Test\test.txt"
Sheets(1).Copy '//Copy sheet 1 to new workbook
ActiveWorkbook.SaveAs sSaveAsFilePath, xlTextWindows '//Save as text (tab delimited) file
If ActiveWorkbook.Name <> ThisWorkbook.Name Then '//Double sure we don't close this workbook
ActiveWorkbook.Close False
End If
Application.DisplayAlerts = True
Application.Wait (Now + TimeValue("0:00:05"))
Loop
End Sub
Sub KillMacro()
StopLoop = True ' stop that perpetual loop in Workbook_Open()
MsgBox "Program Stopped"
End Sub
这会按照得分的顺序为每个对象分配一个“rank”属性,但是你怎么能给绑定的对象提供相同的等级数?
答案 0 :(得分:3)
如果您有幸能够利用一些新的ES6方法,您可以创建一组分数:
var scores = new Set(Object.keys(players).map(function (key) {
return players[key].score;
}));
// > Set {100, 200, 300, 500}
然后将其转换为数组并对其进行排序:
var ordered_scores = Array.from(scores).sort(function(a, b) {
return b - a;
});
// > [500, 300, 200, 100]
然后只是简单地更新你的球员:
Object.keys(players).forEach(function (key) {
var player = players[key];
player.rank = ordered_scores.indexOf(player.score) + 1;
});
var players = {
player1: {
nickname: "Bob",
score: 100
},
player2: {
nickname: "Amy",
score: 200
},
player3: {
nickname: "Grant",
score: 300
},
player4: {
nickname: "Steve",
score: 200
},
player5: {
nickname: "Joe",
score: 500
}
};
var scores = new Set(Object.keys(players).map(function (key) {
return players[key].score;
}));
var ordered_scores = Array.from(scores).sort(function(a, b) {
return b - a;
});
Object.keys(players).forEach(function (key) {
var player = players[key];
player.rank = ordered_scores.indexOf(player.score) + 1;
});
document.write('<pre>' + JSON.stringify(players, null, 2) + '</pre>');
&#13;
答案 1 :(得分:2)
jsfiddle:https://jsfiddle.net/2khtnjxw/1/
// change sort function
array.sort(function(a, b){
return b.score - a.score;
});
var rank = 1;
for (var i = 0; i < array.length; i++) {
// increase rank only if current score less than previous
if (i > 0 && array[i].score < array[i - 1].score) {
rank++;
}
array[i].rank = rank;
}
// result:
[
{
"nickname":"Joe",
"score":500,
"rank":1
},
{
"nickname":"Grant",
"score":300,
"rank":2
},
{
"nickname":"Amy",
"score":200,
"rank":3
},
{
"nickname":"Steve",
"score":200,
"rank":3
},
{
"nickname":"Bob",
"score":100,
"rank":4
}
]
答案 2 :(得分:0)
您正在为键数组指定rank属性,而您需要更新players
对象,并检查相邻的值是否相同以获得相同的排名。你也在排序函数应该改变,以便按降序排序,根据你的代码,它按升序排序。
var players = {
player1: {
nickname: "Bob",
score: 100
},
player2: {
nickname: "Amy",
score: 200
},
player3: {
nickname: "Grant",
score: 300
},
player4: {
nickname: "Steve",
score: 200
},
player5: {
nickname: "Joe",
score: 500
}
};
// get keys from object
var arr = Object.keys(players);
// sort keys array based on score value
arr.sort(function(a, b) {
return players[b].score - players[a].score;
});
// iterate key array and assign rank value to object
for (var i = 0, rank = 1; i < arr.length; i++) {
// assign rank value
players[arr[i]].rank = rank;
// increment rank only if score value is changed
if (players[arr[i + 1]] && players[arr[i]].score != players[arr[i + 1]].score)
rank++;
}
document.write('<pre>' + JSON.stringify(players, null, 3) + '</pre>');
&#13;
答案 3 :(得分:0)
感谢@hamms。
我完成了打字稿(通用)的实现:
export function getRanks<T>(
items: T[],
getId: (item: T) => string,
getScore: (item: T) => number,
): Map<string, number> {
const itemScoresMap = items.reduce(
(r, x) => r.set(getId(x), getScore(x)),
new Map(),
)
const uniqueScores = new Set(itemScoresMap.values())
const orderedScores = Array.from(uniqueScores).sort((a, b) => b - a)
return items
.map(x => {
const id = getId(x)
const score = itemScoresMap.get(id)
return {
id,
rank: orderedScores.indexOf(score) + 1,
}
})
.reduce((r, x) => r.set(x.id, x.rank), new Map())
}