ObjectSort()

Post your working scripts, libraries and tools for AHK v1.1 and older
User avatar
bichlepa
Posts: 183
Joined: 15 Aug 2014, 06:44
Location: Germany
Contact:

ObjectSort()

19 May 2018, 10:33

This is a powerful and easy to use solution for sorting objects.

ObjectSort() sourcecode:

Code: Select all

/* ObjectSort() by bichlepa
* 
* Description:
*    Reads content of an object and returns a sorted array
* 
* Parameters:
*    obj:              Object which will be sorted
*    keyName:          [optional] 
*                      Omit it if you want to sort a array of strings, numbers etc.
*                      If you have an array of objects, specify here the key by which contents the object will be sorted.
*    callBackFunction: [optional] Use it if you want to have custom sort rules.
*                      The function will be called once for each value. It must return a number or string.
*    reverse:          [optional] Pass true if the result array should be reversed
*/
objectSort(obj, keyName="", callbackFunc="", reverse=false)
{
	temp := Object()
	sorted := Object() ;Return value
	
	for oneKey, oneValue in obj
	{
		;Get the value by which it will be sorted
		if keyname
			value := oneValue[keyName]
		else
			value := oneValue
		
		;If there is a callback function, call it. The value is the key of the temporary list.
		if (callbackFunc)
			tempKey := %callbackFunc%(value)
		else
			tempKey := value
		
		;Insert the value in the temporary object.
		;It may happen that some values are equal therefore we put the values in an array.
		if not isObject(temp[tempKey])
			temp[tempKey] := []
		temp[tempKey].push(oneValue)
	}
	
	;Now loop throuth the temporary list. AutoHotkey sorts them for us.
	for oneTempKey, oneValueList in temp
	{
		for oneValueIndex, oneValue in oneValueList
		{
			;And add the values to the result list
			if (reverse)
				sorted.insertAt(1,oneValue)
			else
				sorted.push(oneValue)
		}
	}
	
	return sorted
}
Demonstration script:
You will need string-object-file.ahk. You can get a copy here.

Code: Select all

#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.

#include Object sort.ahk
#include string-object-file.ahk

MyArray:=["Dog", "Cat", "Mouse", "Snake", "Bird"]
MySortedArray:=ObjectSort(MyArray)

MsgBox % "List of animals: `n`n" strobj(MyArray) "`n`nAlphabetically sorted:`n`n" strobj(MySortedArray)

;Source of car list: https://www.globalcarsbrands.com/top-10-fastest-cars-in-the-world/
MyCarList:={}
MyCarList.push({name: "ZENVO ST 1", speedMPH: "233"})
MyCarList.push({name: "HENNESSEY VENOM GT", speedMPH: "270"})
MyCarList.push({name: "KOENIGSEGG AGERA R", speedMPH: "273"})
MyCarList.push({name: "ASTON MARTIN ONE-77", speedMPH: "220"})
MyCarList.push({name: "PAGANI HUAYRA", speedMPH: "230"})
MyCarList.push({name: "MCLAREN F1", speedMPH: "241"})
MyCarList.push({name: "BUGATTI VEYRON SUPER SPORT", speedMPH: "268"})
MyCarList.push({name: "KOENIGSEGG CCR", speedMPH: "242"})
MyCarList.push({name: "SSC ULTIMATE AERO", speedMPH: "256"})
MyCarList.push({name: "9FF GT9-R", speedMPH: "257"})

MySortedCarList:=ObjectSort(MyCarList, "speedMPH",,true)

MsgBox % "Fastest cars: `n`n" strobj(MyCarList) "`n`nSorted by speed:`n`n" strobj(MySortedCarList)

myImmovables:={}
myImmovables.push({town: "New York", size: "60", price: 400000, balcony: 1})
myImmovables.push({town: "Berlin", size: "45", price: 230000, balcony: 1})
myImmovables.push({town: "Moscow", size: "80", price: 350000, balcony: 0})
myImmovables.push({town: "Tokyo", size: "90", price: 600000, balcony: 2})
myImmovables.push({town: "Palma de Mallorca", size: "250", price: 1100000, balcony: 3})

MySortedImmovablesBySize:=ObjectSort(myImmovables, "size")
MySortedImmovablesByBalconyCount:=ObjectSort(myImmovables, "balcony",,true)
MySortedImmovablesPricePerSqM:=ObjectSort(myImmovables, ,"calcPricePerSqM")

calcPricePerSqM(Immovable)
{
	return Immovable.price / Immovable.size
}

MsgBox % "My immovables: `n`n" strobj(myImmovables)
MsgBox % "My immovables sorted by size: `n`n" strobj(MySortedImmovablesBySize)
MsgBox % "My immovables sorted by balcony count: `n`n" strobj(MySortedImmovablesByBalconyCount)
MsgBox % "My immovables sorted by price per square meter: `n`n" strobj(MySortedImmovablesPricePerSqM)
Output of the demonstration script:
Image
ImageImageImageImage
Scripting is too complicated? Try AutoHotFlow, the graphical automation tool! Written in AutoHotkey.
burque505
Posts: 1731
Joined: 22 Jan 2017, 19:37

Re: ObjectSort()

20 May 2018, 09:02

Thank you, bichlepa. Great documentation in the comments, and I'm sure to make use of this.
Regards,
burque505
User avatar
Cerberus
Posts: 172
Joined: 12 Jan 2016, 15:46

Re: ObjectSort()

28 May 2018, 20:03

This looks great. And I'm glad to see very clear examples: a lack of examples often makes it take an hour before I get a script to work. I'm bookmarking this, for I will certainly want to use it in the future.
User avatar
oldbrother
Posts: 273
Joined: 23 Oct 2013, 05:08

Re: ObjectSort()

29 May 2018, 08:02

Thanks for sharing!
doubledave22
Posts: 343
Joined: 08 Jun 2019, 17:36

Re: ObjectSort()

05 Dec 2020, 16:34

Hi, is there any way for this to support decimals? I have an object with values something like 200,0.02, 1.50, 100, 50, 0.25 and this sort does not seem to handle floats. I found a workaround by multiplying the values by 100 and rounding but is there a better way?
fenchai
Posts: 292
Joined: 28 Mar 2016, 07:57

Re: ObjectSort()

14 Dec 2020, 11:11

string-object-file is missing. Should have just copy pasted it in post.
User avatar
watagan
Posts: 80
Joined: 03 Nov 2020, 05:17

Re: ObjectSort()

14 Dec 2020, 20:34

Here is String-object-file.ahk for those who can't find it.

Code: Select all

/*==Description=========================================================================

String-object-file (data structures in YAML-like style)
Author:		Learning one (Boris Mudrinić)
Contact:	https://dl.dropboxusercontent.com/u/171417982/AHK/Learning one contact.png
AHK forum:	http://www.autohotkey.com/board/topic/104854-string-object-file-data-structures-in-yaml-like-style/


=== License ===
Redistribution and use (both commercial and non-commercial) in source and binary forms, are permitted free of charge if you give a credit to the author. Author is not responsible for any damages arising from use or redistribution of his work. If redistributed in source form, you are not allowed to remove comments from this file.


=== See also ===
- Yaml Parser (++JSON) by HotKeyIt: http://www.autohotkey.com/board/topic/65582-ahk-lv2-yaml-yaml-parser-json/
- JSON module by Coco: http://ahkscript.org/boards/viewtopic.php?f=6&t=627&p=4988
- JSON-like (de)serializer by VxE: http://ahkscript.org/boards/viewtopic.php?f=6&t=30&p=99
- Json <---> Object by lordkrandel: http://www.autohotkey.com/board/topic/61328-lib-json-ahk-l-json-object/
- Object from/to file or string by Learning one (obsolete): http://www.autohotkey.com/board/topic/66496-object-fromto-file-or-string-data-structures/
- YAML on wikipedia: http://en.wikipedia.org/wiki/YAML
- Learn Yaml in 5 minutes: http://yaml.codeplex.com/wikipage?title=Yaml in 5 minutes


=== Documentation ===
One function does it all; just call StrObj() function and it will automatically conclude what do you want to do, depending on type of your input.
Here's how to use it and what it does:

Object := StrObj(String)	; String to Object (constructs object from string)
String := StrObj(Obj)		; Object to String (converts object to string)
Object := StrObj(File)		; File to Object (constructs object from file)

ErrorLevel := StrObj(Obj,OutFile)		; saves Object to File
ErrorLevel := StrObj(String,OutFile)	; saves String to File
ErrorLevel := StrObj(File,OutFile)		; saves File to File

Learn from examples.
Warning: although strings which are used to construct objects are in YAML format, there is no full YAML support!
For full YAML support use HotKeyIt's Yaml Parser but be prepared for some surprizes like this: http://www.autohotkey.com/board/topic/65582-ahk-lv2-yaml-yaml-parser-json/page-7#entry640399


Formatting rules for string which are used to construct object;
To build SIMPE ARRAY like ["avocado", "banana", "mango"], use "- value" format (like a list). Your string has to look like this;
	String:= "
	(`
	- avocado
	- banana
	- mango
	)"
	Object := StrObj(String)	; String to Object (constructs object from string)

To build ASSOCIATIVE ARRAY like {FirstName: "John", LastName: "Smith"}, use "key: value" format. Your string has to look like this;
	String:= "
	(`
	FirstName: John	
	LastName: Smith
	)"
	Object := StrObj(String)	; String to Object (constructs object from string)


HIERARCHY in data structure is defined by using indentation, more precisely; tab character. 
So, to build more complex object like {Customer: {FirstName: "John", LastName: "Smith"}, Fruits: ["avocado", "banana", "mango"]}, combine all above like this;
	String:= "
	(`
	Customer:
		FirstName: John	
		LastName: Smith
	Fruits:
		- avocado
		- banana
		- mango
	)"
	Object := StrObj(String)	; String to Object (constructs object from string)


=== Tips for continuation sections ===
If you are using indented continuation sections, always use expression assignment. More info here; http://www.autohotkey.com/board/topic/104735-continuation-sections-left-tabs-in-the-first-line/
Example:
			String:= "
			(`
			- avocado
			- banana
			- mango
			)"

Use accent (`) option in continuation sections;
- it treats each backtick character literally rather than as an escape character.
- it prevents the translation of any explicitly specified escape sequences such as `n and `t.
Example:
	String:= "
	(`
	Name: Item	
	Description: This item does 3 things:`n1) thing 1`n2) thing 2`n3) thing 3
	)"


=== Example 1 - simple array ===
String := "
(`
- avocado
- banana
- mango
)"

Obj := StrObj(String)
MsgBox % Obj.2	; returns "banana"


=== Example 2 - associative array ===
String := "
(`
FirstName: John	
LastName: Smith
Nick: Jonny
)"

Obj := StrObj(String)
MsgBox % Obj.LastName	; returns "Smith"


=== Example 3 - complex ===
String := "
(`
FirstName: John	
LastName: Smith
Nick: Jonny
Parents:
	- Anna
	- Joe
PhoneNumbers:
	-
		Number: 212 555-1234
		Type: fixed
	- 
		Number: 099 555-4567
		Type: mobile
Skills:
	Music: plays guitar
	Programming: knows AHK, C++, HTML
	Spots: good in Kiteboarding, Windsurfing and Swimming
)"
TestFileFullPath := A_ScriptDir "\StrObj test.txt"
OnExit, ExitSub

Obj := StrObj(String)					; String to Object (constructs object from string)

MsgBox % Obj.Parents.2					; returns "Joe"
MsgBox % Obj.PhoneNumbers.MaxIndex()	; returns "2"
MsgBox % Obj.PhoneNumbers.2.Type		; returns "mobile"

Obj.PhoneNumbers.2.Number := "099 123-456"	; sets new value
Obj.Skills.Programming .= ", Lua"			; appends value
Obj.YearOfBirth := 1982						; inserts new key and sets its value

MsgBox % StrObj(Obj)				; converts modified object to a string and displays it in a MsgBox
StrObj(Obj,TestFileFullPath)		; saves modified object to File
Run, % TestFileFullPath
return

F1::
Obj := StrObj(TestFileFullPath)		; File to Object (constructs object from file)
MsgBox % StrObj(Obj)				; converts object to a string and displays it in a MsgBox
return
F2::Run, % TestFileFullPath
Esc::ExitApp
ExitSub:
FileDelete, % TestFileFullPath	; clear testing mess
ExitApp


=== Example 4 - reverse ===
Obj := ["avocado", "banana", "mango"]
MsgBox % StrObj(Obj)	; converts object to string and displays it in MsgBox

Obj := {FirstName: "John", LastName: "Smith"}
MsgBox % StrObj(Obj)	; converts object to string and displays it in MsgBox

Obj := {Customer: {FirstName: "John", LastName: "Smith"}, Fruits: ["avocado", "banana", "mango"]}
MsgBox % StrObj(Obj)	; converts object to string and displays it in MsgBox


=== Example 5 - NewLine and Tab characters ===
String:= "
(`
- avocado
- ba`nna`nna
- man`t`t`tgo
)"
Obj := StrObj(String)	; String to Object

MsgBox % Obj.2
;		MsgBox above displays:
;		ba
;		na
;		na

MsgBox % Obj.3
;		MsgBox above displays:
;		man			go

MsgBox % StrObj(Obj)	; Object to String
;		MsgBox above displays:
;		- avocado
;		- ba`nna`nna
;		- man`t`t`tgo


=== Example 6 - changing default options ===
String := "
(`
Items:
	-
		Action: %A_WinDir%
		Text: Windows
	-
		Action: C:\Script.ahk
		Text: Script
Name: Test menu
)"

Obj := StrObj(String)

;Use default options;
MsgBox % StrObj(Obj)

;Use custom options;
StrObj.Indent := "  "	; change indentation option to 2 spaces. (Default = `t)
MsgBox % StrObj(Obj)
*/


Class StrObj {		; String-object-file (data structures in YAML-like style). By Learning one
	static Version := 1.01, Author := "Learning one", WebSite := "http://www.autohotkey.com/board/topic/104854-string-object-file-data-structures-in-yaml-like-style/"

	static Indent := "`t"
	static EscapedIndent := "``t"			; when converting object to string 
	static NewLine := "`n"					; when parsing a string (reading)
	static EscapedNewLine := "``n"			; when converting object to string 

	static NewLineInOutputString := "`r`n"	; when writing to a string
	static Omit := "`r"						; omitted when parsing a string (reading) 
	static Equal := ":"						; Equal is a sign which delimits key from its value; key: value
	static DerefValues := 1
	static SmartIndentTrim := 1				; useful in [indented continuation sections] and [overindented text in .txt files] which contain YAML-like string
	static FileAppendEncoding := "UTF-8"

	Auto(Input,SaveToFileFullPath="") {						; Automatically concludes what user wants to do. Called by StrObj() function
		Att := FileExist(Input)
		if (Att != "" and InStr(Att, "D") = 0) {			; Input is FILE - user wants to read that file, construct object from its contents (string) and return object
			FileRead, FileContents, % Input
			ReturnValue := this.StrToObj(FileContents)		; returns object
			if (SaveToFileFullPath != "") 					; user actually wants to save String (FileContents) to a file
				ToSave := RTrim(FileContents, " `t`n`r"), SaveToFile := 1
		}
		else if (IsObject(Input) = 1) {						; Input is OBJECT - user wants to convert it to string and return that string
			ReturnValue := this.ObjToStr(Input)				; returns string
			if (SaveToFileFullPath != "")					; user actually wants to save String (ReturnValue) to a file
				ToSave := ReturnValue, SaveToFile := 1	
		}
		else {												; Input is STRING - user wants to construct object from that string and return that object
			ReturnValue := this.StrToObj(Input)				; returns object
			if (SaveToFileFullPath != "")					; user actually wants to save String (Input) to a file
				ToSave := RTrim(Input, " `t`n`r"), SaveToFile := 1
		}
		
		if (SaveToFile = 1) {								; Return value:  0 = no problems (Successful). Anything else = problems (failure). 
			if (FileExist(SaveToFileFullPath) != "") {		; SaveToFileFullPath already exists - delete it first
				FileDelete, % SaveToFileFullPath
				if (ErrorLevel > 0)							; ErrorLevel is set to the number of files that failed to be deleted (if any) or 0 otherwise.
					return ErrorLevel
			}
			FileAppend, % ToSave, % SaveToFileFullPath, % this.FileAppendEncoding
			return ErrorLevel 								; ErrorLevel is set to 1 if there was a problem or 0 otherwise.
		}
		
		return ReturnValue									; if user was not saving to a file, function will return him object or string
	}

	StrToObj(String) {		; Creates object from YAML-like string.
		
		;=== Preparation ===
		Indent := this.Indent
		NewLine := this.NewLine
		Omit := this.Omit
		Equal := this.Equal
		DerefValues := this.DerefValues
		SmartIndentTrim := this.SmartIndentTrim
		obj := [], KeyNames := [], Items := [], LastDepth := 0, CurNum := [0,0,0,0,0,0,0,0,0,0,0,0,0], IndentLen := StrLen(Indent)
		
		;=== SmartIndentTrim ===
		if (SmartIndentTrim = 1) {	; useful in [indented continuation sections] and [overindented text in .txt files] which contain YAML-like string
			Counter := 0, IntentsToTrim := 100000	; IntentsToTrim can be any arbitrary big number... It's unlikely user will have so many tabs  at the left (indentation)
			
			;=== See how many indents at the left have to be trimmed and store it in IntentsToTrim variable ===
			; If you are using indented continuation sections, always use expression assignment. More info here; http://www.autohotkey.com/board/topic/104735-continuation-sections-left-tabs-in-the-first-line/
			; Count only left tabs in the first not-blank line (not in all lines) because first not-blank line must always be at the highest level...
			Loop, parse, String, % NewLine , % Omit
			{
				if A_LoopField is space	; ignore
					continue
				Field := A_LoopField, IndentsInThisLine := 0, FirstLineDetected := 1
				While (SubStr(Field,1,IndentLen) = Indent) {
					Field := SubStr(Field, IndentLen+1)	; removes first %IndentLen% characters
					IndentsInThisLine += 1
				}
				if (IndentsInThisLine < IntentsToTrim)
					IntentsToTrim := IndentsInThisLine
				if (FirstLineDetected = 1)
					break
			}
			
			;=== If there are extra indents in this string to trim at the left ===
			if (IntentsToTrim < 100000 and IntentsToTrim > 0) {		; there are extra indents in this string to trim at the left
				NewString := ""
				Loop, parse, String, % NewLine , % Omit
				{
					Field := SubStr(A_LoopField, IntentsToTrim+1)	; removes first %IntentsToTrim% characters
					NewString .= Field NewLine
				}
				String := SubStr(NewString,1, StrLen(NewString)-StrLen(NewLine))	; overwrite String with NewString, which hasn't got extra indents at the left
			}
		}
		
		;=== Extract data from string ===
		Loop, parse, String, % NewLine , % Omit
		{
			CurDepth := 1, IsPreviousItemValueObject := 0
			if A_LoopField is space
				continue
			Field := RTrim(A_LoopField, " `t`r")
			While (SubStr(Field,1,IndentLen) = Indent) {
				Field := SubStr(Field, IndentLen+1)	; removes first %IndentLen% characters
				CurDepth += 1
			}
			
			if (CurDepth != LastDepth) {	; Indent change
				if (CurDepth < LastDepth)	; <--- Decreased indent
					CurNum[LastDepth] := 0	; 		restart numbering for LastDepth
				if (CurDepth > LastDepth) {	; ---> Increased indent
					CurNum[CurDepth] := 0	; 		restart numbering for CurDepth
					if (CurDepth > 1)
						IsPreviousItemValueObject := 1
				}
				LastDepth := CurDepth
			}
			
			if (SubStr(Field,1,1) = "-") {		; if FirstChar is "-"
				CurNum[CurDepth] += 1
				NewItem := CurNum[CurDepth]  ": " Trim(SubStr(Field,2), " `t`r")	; Exa: "- Joe" --> "2: Joe"
			}
			else
				NewItem := Field	; Exa: "FirstName: John"
			
			EqualPos := InStr(NewItem, Equal)
			k := SubStr(NewItem, 1, EqualPos-1), v := SubStr(NewItem, EqualPos+1)
			k := Trim(k, " `t`r"), v := Trim(v, " `t`r")	; k=key, v=value
			
			if (DerefValues = 1)
				Transform, v, Deref, % v
			
			KeyNames[CurDepth] := k
			
			DepthNames := []
			Loop, % CurDepth
				DepthNames.Insert(KeyNames[A_Index])	; DepthNames exa: ["PhoneNumbers", "1", "Type"]
			
			Items.Insert([DepthNames,v])				; Items structure: DepthNames,value
			if (IsPreviousItemValueObject = 1) {
				PreviousItemNum := Items.MaxIndex() - 1
				Items[PreviousItemNum].2 := []			; value becomes object
			}		
		}
		
		;=== Construct object ===
		For k,v in Items	; Items structure: DepthNames,value
		{
			n := v.1			; n = DepthNames. Exa: ["PhoneNumbers", "1", "Type"]
			value := v.2		; values. Exa: "Joe"
			
			if value is Integer
				value := value*1	;  assigns a pure number instead of string - important for some COM methods
			
			CurLevel := n.MaxIndex()
			if (CurLevel = 1)
				obj[n.1] := value
			else if (CurLevel = 2)
				obj[n.1][n.2] := value
			else if (CurLevel = 3)
				obj[n.1][n.2][n.3] := value
			else if (CurLevel = 4)
				obj[n.1][n.2][n.3][n.4] := value
			else if (CurLevel = 5)
				obj[n.1][n.2][n.3][n.4][n.5] := value
			else if (CurLevel = 6)
				obj[n.1][n.2][n.3][n.4][n.5][n.6] := value
			else if (CurLevel = 7)
				obj[n.1][n.2][n.3][n.4][n.5][n.6][n.7] := value
			else if (CurLevel = 8)
				obj[n.1][n.2][n.3][n.4][n.5][n.6][n.7][n.8] := value
			else if (CurLevel = 9)
				obj[n.1][n.2][n.3][n.4][n.5][n.6][n.7][n.8][n.9] := value	
			else if (CurLevel = 10)
				obj[n.1][n.2][n.3][n.4][n.5][n.6][n.7][n.8][n.9][n.10] := value	
			else if (CurLevel = 11)
				obj[n.1][n.2][n.3][n.4][n.5][n.6][n.7][n.8][n.9][n.10][n.11] := value	
			else if (CurLevel = 12)
				obj[n.1][n.2][n.3][n.4][n.5][n.6][n.7][n.8][n.9][n.10][n.11][n.12] := value	; etc
		}
		return obj
	}
	
	ObjToStr(Obj, Depth=12, CurIndent="") {	; Converts object to YAML-like string.
		For k,v in Obj
		{
			if (IsObject(v) = 1 and Depth>1 ) {
				middlepart := this.NewLineInOutputString StrObj.ObjToStr(v, Depth-1, CurIndent this.Indent)
				;~ if k is Integer ;Modified by bichlepa
					;~ ToReturn .= CurIndent "-" A_Space middlepart
				;~ else {
					StringReplace, k, k, % this.Indent, % this.EscapedIndent, all
					StringReplace, k, k, % this.NewLine, % this.EscapedNewLine, all										
					ToReturn .= CurIndent k this.Equal A_Space middlepart
				;~ }				
			}
			else {
				StringReplace, v, v, % this.Indent, % this.EscapedIndent, all
				StringReplace, v, v, % this.NewLine, % this.EscapedNewLine, all
				
				;~ if k is Integer  ;Modified by bichlepa
					;~ ToReturn .= CurIndent "-" A_Space v this.NewLineInOutputString
				;~ else {
					StringReplace, k, k, % this.Indent, % this.EscapedIndent, all
					StringReplace, k, k, % this.NewLine, % this.EscapedNewLine, all										
					ToReturn .= CurIndent k this.Equal A_Space v this.NewLineInOutputString
				;~ }
			}
		}
		return RTrim(ToReturn, NewLineInOutputString)
	}	; http://www.autohotkey.com/forum/post-426623.html#426623	
}

StrObj(Input,SaveToFileFullPath="") {					; Part of [Class StrObj]
	return StrObj.Auto(Input,SaveToFileFullPath)
}
User avatar
Cerberus
Posts: 172
Joined: 12 Jan 2016, 15:46

Re: ObjectSort()

30 Mar 2021, 23:45

watagan wrote:
14 Dec 2020, 20:34
Here is String-object-file.ahk for those who can't find it.
...
Thank you for posting this.
KiddoV
Posts: 13
Joined: 11 May 2020, 20:29

Re: ObjectSort()

17 May 2021, 14:39

Good function but it is still confused about "A11" and "A102" for example. It would put A102 first then A11. Is there anyway to fix it?
Test:

Code: Select all

testObj := [{ref: "A10"}, {ref: "A12"}, {ref: "A11"}, {ref: "A102"}, {ref: "B3"}, {ref: "B0"}]
The result:

Code: Select all

resultObj := [{"ref":"A10"}, {"ref":"A102"}, {"ref":"A11"}, {"ref":"A12"}, {"ref":"B0"}, {"ref":"B3"}]
Thanks,
User avatar
rommmcek
Posts: 1470
Joined: 15 Aug 2014, 15:18

Re: ObjectSort()

23 May 2021, 15:55

For the special case (left part of the value are letters, right part are numbers) you can try something like:

Code: Select all

testObj := [{ref: "A10"}, {ref: "A12"}, {ref: "A11"}, {ref: "A102"}, {ref: "B3"}, {ref: "B0"}]

for i, j in (testObj, srr:= [])
    for k, l in j
        an:=  RegExReplace(l, "[0-9]"), nu:= RegExReplace(l, "[^0-9]")
      , srr[an]? "": srr[an]:=[], srr[an][nu]? "": srr[an][nu]:=[], srr[an][nu].Push(l)
pa(srr)

pa(in) {
    MsgBox % txtArr(in)
}

txtArr(r, i:="  ", d:=0, b:="") {
	For k, v in (r, IsObject(r)? "": e:= 1)
		c.= IsObject(v)? b k ":`n" txtArr(v, i, d+1, b i): b k ": " v "`n", d>0? "": t:=c
    Return e? r: d>0? c: t
}
[Addendum]: After couple of (local) questions how to get previous array format:

Code: Select all

testObj := [{ref: "A10"}, {ref: "A12"}, {ref: "A11"}, {ref: "A102"}, {ref: "B3"}, {ref: "B0"}]
;pa(testObj) ; display initial array

for i, j in (testObj, srr:= [])
    for k, l in (j, ij:=j)
        an:=  RegExReplace(l, "[0-9]"), nu:= RegExReplace(l, "[^0-9]")
      , srr[an]? "": srr[an]:=[], srr[an][nu]? "": srr[an][nu]:=[], srr[an][nu].Push(ij)
;pa(srr) ; display sorted array (nested)

for i, j in (srr, frr:=[])
    for k, l in j
        for m, n in l
            frr.Push(n)
pa(frr) ; display final array

pa(in) {
    MsgBox % txtArr(in)
}

txtArr(r, i:="  ", d:=0, b:="") {
	For k, v in (r, IsObject(r)? "": e:= 1)
		c.= IsObject(v)? b k ":`n" txtArr(v, i, d+1, b i): b k ": " v "`n", d>0? "": t:=c
    Return e? r: d>0? c: t
}
murataygun
Posts: 104
Joined: 07 Aug 2015, 15:53

Re: ObjectSort()

30 Sep 2022, 03:59

Thank you.
Malzemeler["item1"] := {id: 1, name: "deneme", adet: 3}
Malzemeler["item2"] := {id: 1, name: "deneme", adet: 3}

If i sort this array i loose Malzemeler arrays keys. (item1, item2)
User avatar
rommmcek
Posts: 1470
Joined: 15 Aug 2014, 15:18

Re: ObjectSort()

30 Sep 2022, 10:08

Code: Select all

#Persistent
#SingleInstance, force

Malzemeler := Object()

Malzemeler["glow"] := {color: "red", size: "L", code: 123, isAvailable: 1, id: 1}
Malzemeler["shirt"] := {color: "green", size: "S", code: 321, isAvailable: 1, id: 2}
Malzemeler["sock"] := {color: "blue", size: "xL", code: 789, isAvailable: 1, id: 3}
Malzemeler["pants"] := {color: "purple", size: "M", code: 987, isAvailable: 1, id: 4}

;test Malzemeler
pa(Malzemeler)

;Sort
for i, j in (Malzemeler, srr:=[])
   srr[Malzemeler[i].id]? "": srr[Malzemeler[i].id]:=[], srr[Malzemeler[i].id].Push(i), srr[Malzemeler[i].id].Push(j)

;test srr
pa(srr)
;this works too
MsgBox, % srr.1.1 "`n" srr.1.2.color
Return
Esc:: ExitApp

pa(in) {
    MsgBox % txtArr(in)
}

txtArr(r, i:="  ", d:=0, b:="") {
	For k, v in (r, IsObject(r)? "": e:= 1)
		c.= IsObject(v)? b k ":`n" txtArr(v, i, d+1, b i): b k ": " v "`n", d>0? "": t:=c
    Return e? r: d>0? c: t
}
function1:
function2:
Last edited by rommmcek on 04 Oct 2022, 13:55, edited 5 times in total.
murataygun
Posts: 104
Joined: 07 Aug 2015, 15:53

Re: ObjectSort()

30 Sep 2022, 15:44

rommmcek wrote:
30 Sep 2022, 10:08
If you mean:
Thank you for your reply. Its so kind of you spend time coding that loops.
But its not the case. Maybe i can express stuation in code. Please see the code comments. Its working code to easly copy/paste and run.

Code: Select all

#Persistent
#SingleInstance, force

Malzemeler := Object()

Malzemeler["glow"] := {color: "red", size: "L", code: 123, isAvailable: 1, id: 1}
Malzemeler["shirt"] := {color: "green", size: "S", code: 321, isAvailable: 1, id: 2}
Malzemeler["sock"] := {color: "blue", size: "xL", code: 789, isAvailable: 1, id: 3}
Malzemeler["pants"] := {color: "purple", size: "M", code: 987, isAvailable: 1, id: 4}

;Test if this works. It works..
MsgBox, % Malzemeler.glow.color

;Sort
tmpMalzemeler := objectSort(Malzemeler, "id", "", true)

;This works but ...
MsgBox, % tmpMalzemeler[4].color
;This doesnt.. We lost the key "glow" and the others. "shirt", "sock"  etc.
MsgBox, % tmpMalzemeler.glow.color

return
objectSort(obj, keyName="", callbackFunc="", reverse=false)
    {
        temp := Object()
        sorted := Object() ;Return value
        
        for oneKey, oneValue in obj
        {
            ;Get the value by which it will be sorted
            if keyname
                value := oneValue[keyName]
            else
                value := oneValue
            
            ;If there is a callback function, call it. The value is the key of the temporary list.
            if (callbackFunc)
                tempKey := %callbackFunc%(value)
            else
                tempKey := value
            
            ;Insert the value in the temporary object.
            ;It may happen that some values are equal therefore we put the values in an array.
            if not isObject(temp[tempKey])
                temp[tempKey] := []
            temp[tempKey].push(oneValue)
        }
        
        ;Now loop throuth the temporary list. AutoHotkey sorts them for us.
        for oneTempKey, oneValueList in temp
        {
            for oneValueIndex, oneValue in oneValueList
            {
                ;And add the values to the result list
                if (reverse)
                    sorted.insertAt(1,oneValue)
                else
                    sorted.push(oneValue)
            }
        }
        
        return sorted
    }
User avatar
rommmcek
Posts: 1470
Joined: 15 Aug 2014, 15:18

Re: ObjectSort()

30 Sep 2022, 22:49

murataygun wrote:
30 Sep 2022, 15:44
...its not the case..
It's not that much different, but I blundered immensely!
Edited previous post.
Is that good for you or do you require original array format and reverse order?
User avatar
joedf
Posts: 8940
Joined: 29 Sep 2013, 17:08
Location: Canada
Contact:

Re: ObjectSort()

01 Oct 2022, 07:03

This reminds me of my Autosort function, that just takes advantage of ahk's sorting: viewtopic.php?t=3790

I dunno if it's the same, here it is anyway :mrgreen:
Image Image Image Image Image
Windows 10 x64 Professional, Intel i5-8500, NVIDIA GTX 1060 6GB, 2x16GB Kingston FURY Beast - DDR4 3200 MHz | [About Me] | [About the AHK Foundation] | [Courses on AutoHotkey]
[ASPDM - StdLib Distribution] | [Qonsole - Quake-like console emulator] | [LibCon - Autohotkey Console Library]
User avatar
rommmcek
Posts: 1470
Joined: 15 Aug 2014, 15:18

Re: ObjectSort()

01 Oct 2022, 09:16

Didn't know about AutoSort. I refered to one-loop sorting. The idea is the same, however murataygun has a nested array. Having the function is useful, so I added two versions in my previous post.
User avatar
joedf
Posts: 8940
Joined: 29 Sep 2013, 17:08
Location: Canada
Contact:

Re: ObjectSort()

01 Oct 2022, 09:54

One-loop sorting... interesting :think:
Image Image Image Image Image
Windows 10 x64 Professional, Intel i5-8500, NVIDIA GTX 1060 6GB, 2x16GB Kingston FURY Beast - DDR4 3200 MHz | [About Me] | [About the AHK Foundation] | [Courses on AutoHotkey]
[ASPDM - StdLib Distribution] | [Qonsole - Quake-like console emulator] | [LibCon - Autohotkey Console Library]
murataygun
Posts: 104
Joined: 07 Aug 2015, 15:53

Re: ObjectSort()

02 Oct 2022, 12:07

rommmcek wrote:
01 Oct 2022, 09:16
Didn't know about AutoSort. I refered to one-loop sorting. The idea is the same, however murataygun has a nested array. Having the function is useful, so I added two versions in my previous post.
Thank you.

I have that nested array. Have 44 objects in it. I build a table from it with the help of neutron.ahk. Users can change the values etc. Most used items must be on top. So, i sorted them manually by setting their id parameter.

But when pull them from API Autohotkey sorts it automatically (alphabetically) by key names. So my table messes up. But in app flow i should still want to use that array associatively.

like;

Code: Select all

Malzemeler.shirt.color
In your solutions Malzemeler array untouched.
"srr" array sorted but can't use it like "srr.shirt.color"

unsorted Malzemeler --->> | sort box | ---->> sorted Malzemeler array which i can use Malzemeler.shirt.color

Maybe its not that easy. Thank you very much for your time and efforts. :angel:
murataygun
Posts: 104
Joined: 07 Aug 2015, 15:53

Re: ObjectSort()

02 Oct 2022, 12:40

BTW.. Why the heck AHK sorts arrays? :D
User avatar
rommmcek
Posts: 1470
Joined: 15 Aug 2014, 15:18

Re: ObjectSort()

04 Oct 2022, 14:04

The best I could do is srr[GetProperty(srr, "shirt")].shirt.color or srr[GetProperty(srr, "shirt")].2.color.
I updated the two functions in previous post with code.

P.s.: Why do you wanna have such array sorted like this? To visually inspect the content you can use one of the functions I posted. but to change the array you dont need sorting, you can use original array with usual syntax...

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: No registered users and 115 guests