Page 1 of 1
Numerically AND Alphabetically sorting data
Posted: 04 Jun 2018, 19:54
by Saiftey
Hi, I need to sort my data
first numerically
then alphabetically. This seems quite complex, I'm having a huge struggle trying to figure out this because my input's beginning of the lines don't start with the numbers/text.. any ideas on a script?
My input
- * 1 [[Bakelelite]]
* 60 [[Rose of the Sands]]
* 1 [[Fine Sand]]
* 4 [[Ourobubble Scales]]
* 11 [[Scordion Tail]]
* 11 [[Mantiscore Carapace]]
My desired output
- * 60 [[Rose of the Sands]]
* 11 [[Mantiscore Carapace]]
* 11 [[Scordion Tail]]
* 4 [[Ourobubble Scales]]
* 1 [[Bakelelite]]
* 1 [[Fine Sand]]
If you could give me an example at the very least that would be great, I learn from seeing. Thanks.
Re: Numerically AND Alphabetically sorting data
Posted: 04 Jun 2018, 21:27
by Saiftey
I just realized.. i suppose I could remove the * just for this instance then at the end do a regex replace to add the * back in if it makes it easier.. let me hunt on the forums for a solution now..
Re: Numerically AND Alphabetically sorting data
Posted: 04 Jun 2018, 22:27
by wolf_II
Try this:
Code: Select all
#NoEnv
#SingleInstance, Force
MyInput =
(
* 1 [[Bakelelite]]
* 60 [[Rose of the Sands]]
* 1 [[Fine Sand]]
* 4 [[Ourobubble Scales]]
* 11 [[Scordion Tail]]
* 11 [[Mantiscore Carapace]]
)
; loop through MyInput to produce {MyData} and [Numbers]
MyData := {}, Numbers := []
Loop, Parse, MyInput, `n, `r
If MyData.HasKey(n := ParseLine(A_LoopField, Item))
MyData[n].Push(Item)
Else ; new number
MyData[InsertNumber(n)] := [Item]
; loop through [Numbers] to produce "DesiredOutput"
For each, Num in Numbers
DesiredOutput .= CollectFrom(Num)
MsgBox, %DesiredOutput%
ExitApp
;-------------------------------------------------------------------------------
CollectFrom(n) { ; return string with sorted Items
;-------------------------------------------------------------------------------
global MyData
For each, Item in MyData[n]
Result .= "* " n " [[" Item "]]`n"
Sort, Result
Return, Result
}
;-------------------------------------------------------------------------------
InsertNumber(NewNumber) { ; keeps [Numbers] in descending order
;-------------------------------------------------------------------------------
global Numbers
If !Numbers.Length()
Numbers := [NewNumber]
Else {
For Index, n in Numbers
If (NewNumber < n)
Continue
Else
Break
Numbers.InsertAt(Index, NewNumber)
}
Return, NewNumber
}
;-------------------------------------------------------------------------------
ParseLine(Haystack, ByRef Item) { ; return the number part of Haystack
;-------------------------------------------------------------------------------
RegExMatch(Haystack, " (\d+) \[\[(.*)\]\]", match)
Return, Match1, Item := match2
}
I hope that helps.
Re: Numerically AND Alphabetically sorting data
Posted: 04 Jun 2018, 23:35
by jeeswg
- A custom sort function can often make tasks like this easier.
Code: Select all
q:: ;sort 2 columns
vText = ;continuation section
(
* 1 [[Bakelelite]]
* 60 [[Rose of the Sands]]
* 1 [[Fine Sand]]
* 4 [[Ourobubble Scales]]
* 11 [[Scordion Tail]]
* 11 [[Mantiscore Carapace]]
)
Sort, vText, F Sort2Cols
MsgBox, % vText
return
;for a custom sort function: CustomSort(item1, item2, offset)
;if item1 should go earlier in list, return negative e.g. -1
;if item2 should go earlier in list, return positive e.g. 1
;if items equal, and items should maintain their order, return -offset [stable sort]
;if items equal, and items should swap their order, return offset [stable sort]
;if items equal, and order doesn't matter, return 0 [unstable sort]
;note: if item1 was earlier in original list, offset is positive
;note: if item2 was earlier in original list, offset is negative
;sort by column 1 (from the first digit to the last consecutive digit)
;sort by column 2 (from the '[[' onwards)
Sort2Cols(vTextA, vTextB, vOffset)
{
RegExMatch(vTextA, "O).*?\K(\d+).*?(\Q[[\E.*)", oMatch1)
RegExMatch(vTextB, "O).*?\K(\d+).*?(\Q[[\E.*)", oMatch2)
vNumA := oMatch1.1, vNumB := oMatch2.1
;ascending order (numerical):
;if vRet := (vNumA > vNumB) ? 1 : (vNumA < vNumB) ? -1 : 0
;descending order (numerical):
if vRet := (vNumA < vNumB) ? 1 : (vNumA > vNumB) ? -1 : 0
return vRet
vTextA := oMatch1.2, vTextB := oMatch2.2
;asecending order (text):
return ("" vTextA > vTextB) ? 1 : ("" vTextA < vTextB) ? -1 : -vOffset
;descecending order (text):
;return ("" vTextA < vTextB) ? 1 : ("" vTextA > vTextB) ? -1 : -vOffset
}
- Another idea would be to parse each line, adding in leading zeros, to sort the lines, and to remove the leading zeros.
Re: Numerically AND Alphabetically sorting data
Posted: 04 Jun 2018, 23:58
by wolf_II
jeeswg wrote:Another idea would be to parse each line, adding in leading zeros, to sort the lines, and to remove the leading zeros.
That's a brilliant idea!
Code: Select all
MyInput =
(
* 1 [[Bakelelite]]
* 60 [[Rose of the Sands]]
* 1 [[Fine Sand]]
* 4 [[Ourobubble Scales]]
* 11 [[Scordion Tail]]
* 11 [[Mantiscore Carapace]]
)
Loop, Parse, MyInput, `n, `r
Output .= StrReplace(A_LoopField, "* ", "00") "`n"
Sort, Output, RN
MsgBox, % StrReplace(Output, "00", "* ")
Re: Numerically AND Alphabetically sorting data
Posted: 05 Jun 2018, 07:34
by Saiftey
jeeswg wrote:- A custom sort function can often make tasks like this easier.
Code: Select all
q:: ;sort 2 columns
vText = ;continuation section
(
* 1 [[Bakelelite]]
* 60 [[Rose of the Sands]]
* 1 [[Fine Sand]]
* 4 [[Ourobubble Scales]]
* 11 [[Scordion Tail]]
* 11 [[Mantiscore Carapace]]
)
Sort, vText, F Sort2Cols
MsgBox, % vText
return
;for a custom sort function: CustomSort(item1, item2, offset)
;if item1 should go earlier in list, return negative e.g. -1
;if item2 should go earlier in list, return positive e.g. 1
;if items equal, and items should maintain their order, return -offset [stable sort]
;if items equal, and items should swap their order, return offset [stable sort]
;if items equal, and order doesn't matter, return 0 [unstable sort]
;note: if item1 was earlier in original list, offset is positive
;note: if item2 was earlier in original list, offset is negative
;sort by column 1 (from the first digit to the last consecutive digit)
;sort by column 2 (from the '[[' onwards)
Sort2Cols(vTextA, vTextB, vOffset)
{
RegExMatch(vTextA, "O).*?\K(\d+).*?(\Q[[\E.*)", oMatch1)
RegExMatch(vTextB, "O).*?\K(\d+).*?(\Q[[\E.*)", oMatch2)
vNumA := oMatch1.1, vNumB := oMatch2.1
;ascending order (numerical):
;if vRet := (vNumA > vNumB) ? 1 : (vNumA < vNumB) ? -1 : 0
;descending order (numerical):
if vRet := (vNumA < vNumB) ? 1 : (vNumA > vNumB) ? -1 : 0
return vRet
vTextA := oMatch1.2, vTextB := oMatch2.2
;asecending order (text):
return ("" vTextA > vTextB) ? 1 : ("" vTextA < vTextB) ? -1 : -vOffset
;descecending order (text):
;return ("" vTextA < vTextB) ? 1 : ("" vTextA > vTextB) ? -1 : -vOffset
}
- Another idea would be to parse each line, adding in leading zeros, to sort the lines, and to remove the leading zeros.
Hi, so I have been testing out this on another input list that has a slight variation, this time only needing to alphabetise the list - It doesn't seem to come out right, what am I doing wrong?
Code: Select all
^F1::
vText = ;continuation section
(
* [[Black Dreggheadgear]]
* [[The Xyothine]]
* [[Black Mel Root]]
* [[Yondanwa Staff]]
* [[Kape Axe]]
* [[Ancestral Treecape]]
* [[Ancestral Torc]]
* [[Ancestral Treechelt]]
* [[Spanner's Wrench]]
* [[Ancestral Treechnid Essence]]
)
Sort, vText, F Sort2Cols
MsgBox, % vText
return
;for a custom sort function: CustomSort(item1, item2, offset)
;if item1 should go earlier in list, return negative e.g. -1
;if item2 should go earlier in list, return positive e.g. 1
;if items equal, and items should maintain their order, return -offset [stable sort]
;if items equal, and items should swap their order, return offset [stable sort]
;if items equal, and order doesn't matter, return 0 [unstable sort]
;note: if item1 was earlier in original list, offset is positive
;note: if item2 was earlier in original list, offset is negative
;sort by column 1 (from the first digit to the last consecutive digit)
;sort by column 2 (from the '[[' onwards)
Sort2Cols(vTextA, vTextB, vOffset)
{
RegExMatch(vTextA, "O).*?\K(\Q[[\E.*)", oMatch1)
RegExMatch(vTextB, "O).*?\K(\Q[[\E.*)", oMatch2)
;vNumA := oMatch1.1 , vNumB := oMatch2.1
;ascending order (numerical):
;if vRet := (vNumA > vNumB) ? 1 : (vNumA < vNumB) ? -1 : 0
;descending order (numerical):
;if vRet := (vNumA < vNumB) ? 1 : (vNumA > vNumB) ? -1 : 0
;return vRet
vTextA := oMatch1.2, vTextB := oMatch2.2
;asecending order (text):
return ("" vTextA > vTextB) ? 1 : ("" vTextA < vTextB) ? -1 : -vOffset
;descecending order (text):
;return ("" vTextA < vTextB) ? 1 : ("" vTextA > vTextB) ? -1 : -vOffset
}
return
esc::
exitapp
Re: Numerically AND Alphabetically sorting data
Posted: 05 Jun 2018, 20:17
by jeeswg
Replace:
vTextA := oMatch1.2, vTextB := oMatch2.2
with:
vTextA := oMatch1.1, vTextB := oMatch2.1
In this simpler example, key '1' for each RegExMatch object contains the text from the column. In the 2-column example, key '1' contains the number, key '2' contains the text.
Hmm, maybe I should have called the objects oMatchA and oMatchB, it might have made things clearer.
In fact, for this example, you don't need a custom function. You can replace:
Sort, vText, F Sort2Cols
with:
Sort, vText
I.e. you just sort the lines alphabetically.
Re: Numerically AND Alphabetically sorting data
Posted: 05 Jun 2018, 20:30
by jeeswg
I tried the 'add leading zeros' approach, but it doesn't quite work.
The numbers end up in the correct order, but the strings end up in reverse order.
Code: Select all
q:: ;sort (add leading zeros, sort, remove leading zeros)
vText = ;continuation section
(
* 1 [[Bakelelite]]
* 60 [[Rose of the Sands]]
* 1 [[Fine Sand]]
* 4 [[Ourobubble Scales]]
* 11 [[Scordion Tail]]
* 11 [[Mantiscore Carapace]]
)
VarSetCapacity(vOutput, StrLen(vText)*2)
Loop, Parse, vText, `n, `r
{
if (InStr(A_LoopField, "[[") = 5)
vOutput .= (A_Index=1?"":"`n") StrReplace(A_LoopField, "* ", "* 0")
else
vOutput .= (A_Index=1?"":"`n") A_LoopField
}
MsgBox, % vOutput
Sort, vOutput, R
vOutput := RegExReplace(vOutput, "\* \K0+")
MsgBox, % vOutput
return
Re: Numerically AND Alphabetically sorting data
Posted: 05 Jun 2018, 20:44
by jeeswg
@wolf_II: Thanks, I tried your script, but it suffers from the same problem, column 1 is in the correct order, column 2 is in reverse order.
Re: Numerically AND Alphabetically sorting data
Posted: 05 Jun 2018, 20:53
by wolf_II
@jeeswg Yes, I noticed that too late, sorry.
Re: Numerically AND Alphabetically sorting data
Posted: 05 Jun 2018, 20:57
by jeeswg
- In theory, this script would work, however, AHK doesn't have built-in handling for stable sort. (I had already added stable sort to my Wish List 2.0.)
- So it might not work exactly right.
- I.e. when it sees two items as having the same value, instead of preserving the order, it allows itself to reorder the items.
Code: Select all
q:: ;sort (add leading zeros, sort, remove leading zeros)
vText = ;continuation section
(
* 1 [[Bakelelite]]
* 60 [[Rose of the Sands]]
* 1 [[Fine Sand]]
* 4 [[Ourobubble Scales]]
* 11 [[Scordion Tail]]
* 11 [[Mantiscore Carapace]]
)
VarSetCapacity(vOutput, StrLen(vText)*2)
Loop, Parse, vText, `n, `r
{
if (InStr(A_LoopField, "[[") = 5)
vOutput .= (A_Index=1?"":"`n") "00000" SubStr(A_LoopField, 3)
else
vOutput .= (A_Index=1?"":"`n") "0000" SubStr(A_LoopField, 3)
}
MsgBox, % vOutput
Sort, vOutput, P7
MsgBox, % vOutput
Sort, vOutput, NR
vOutput := StrReplace(vOutput, "00000", "* ")
vOutput := StrReplace(vOutput, "0000", "* ")
MsgBox, % vOutput
return
Re: Numerically AND Alphabetically sorting data
Posted: 07 Jun 2018, 07:40
by Saiftey
When i put %clipboard% as my vtext it doesnt work , but when i put the actual raw input , it works - any idea why this is?