Numerically AND Alphabetically sorting data

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
Saiftey
Posts: 18
Joined: 08 May 2018, 18:30

Numerically AND Alphabetically sorting data

04 Jun 2018, 19:54

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.
Saiftey
Posts: 18
Joined: 08 May 2018, 18:30

Re: Numerically AND Alphabetically sorting data

04 Jun 2018, 21:27

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..
wolf_II
Posts: 2688
Joined: 08 Feb 2015, 20:55

Re: Numerically AND Alphabetically sorting data

04 Jun 2018, 22:27

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.
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Numerically AND Alphabetically sorting data

04 Jun 2018, 23:35

- 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.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
wolf_II
Posts: 2688
Joined: 08 Feb 2015, 20:55

Re: Numerically AND Alphabetically sorting data

04 Jun 2018, 23:58

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", "* ")
Saiftey
Posts: 18
Joined: 08 May 2018, 18:30

Re: Numerically AND Alphabetically sorting data

05 Jun 2018, 07:34

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
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Numerically AND Alphabetically sorting data

05 Jun 2018, 20:17

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.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Numerically AND Alphabetically sorting data

05 Jun 2018, 20:30

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
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Numerically AND Alphabetically sorting data

05 Jun 2018, 20:44

@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.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
wolf_II
Posts: 2688
Joined: 08 Feb 2015, 20:55

Re: Numerically AND Alphabetically sorting data

05 Jun 2018, 20:53

@jeeswg Yes, I noticed that too late, sorry.
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Numerically AND Alphabetically sorting data

05 Jun 2018, 20:57

- 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
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Saiftey
Posts: 18
Joined: 08 May 2018, 18:30

Re: Numerically AND Alphabetically sorting data

07 Jun 2018, 07:40

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?

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: doodles333, Google [Bot], iamMG and 170 guests