help with uniq() for keyed values Topic is solved

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
User avatar
Chunjee
Posts: 1397
Joined: 18 Apr 2014, 19:05
Contact:

help with uniq() for keyed values

23 Dec 2018, 16:15

I'm having trouble getting the behavior I need from this function:

Code: Select all

uniq(arr) { ; Hash O(n) 

    hash := {}, newArr := []

    for e, v in arr
    {
        if !(hash[v]) {
            hash[(v)] := 1, newArr.push(v)
        }
    }
    return newArr
}

Code: Select all

TestArray := ["a","a","a","b","b",{"hello":"friend"},{"hello":"friend"}]
uniq(TestArray)
;Expecting => ["a","b",{"hello":"friend"}]

;Actual => ["a","b",{"hello":"friend"},{"hello":"friend"}]
TAC109
Posts: 1096
Joined: 02 Oct 2013, 19:41
Location: New Zealand

Re: help with uniq() for keyed values

23 Dec 2018, 22:49

Although the 6th and 7th entries in your testarray contain arrays which contains the same keys and values, this is by coincidence only; AHK will have created separate arrays. To make them the same, code as follows (untested):-

Code: Select all

Test1:={"hello":"friend"}
TestArray := ["a","a","a","b","b",Test1,Test1]
uniq(TestArray)[/code]
My scripts:-
XRef - Produces Cross Reference lists for scripts
ReClip - A Text Reformatting and Clip Management utility
ScriptGuard - Protects Compiled Scripts from Decompilation
I also maintain Ahk2Exe
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: help with uniq() for keyed values

24 Dec 2018, 00:49

stolen from https://autohotkey.com/boards/viewtopic ... 99#p189199

Code: Select all

TestArray := ["a","a","a","b","b",{"hello":"friend"},{"hello":"friends"}]
newArr := uniq(TestArray)
;Expecting => ["a","b",{"hello":"friend"}]


uniq(arr) { ; Hash O(n)

	hash := {}
	newArr := []
	CachedObjects := []

	for each, element in arr
	{
		if IsObject(element)
		{
			size := ObjDump(element, dumped)

			if (CachedObjects.Count() == 0)
			{
				CachedObjects.Push({"ptr_data": &dumped, "size": size})
				newArr.Push(element)
			}
			else
			{
				for each, Cached in CachedObjects
				{
					if DllCall("msvcrt\memcmp", "Ptr", &dumped, "Ptr", Cached.ptr_data, "Int", Cached.size)
					{
						CachedObjects.Push({"ptr_data": &dumped, "size": size})
						newArr.Push(element)
					}
				}
			}
		}
		else if !(hash[element])
		{
			hash[element] := 1
			newArr.Push(element)
		}
	}
	return newArr
}

ObjDump(obj,ByRef var:="",mode:=0){
  If IsObject(var){ ; FileAppend mode
	If FileExist(obj){
	  FileDelete,%obj%
	  If ErrorLevel
		return
	}
	f:=FileOpen(obj,"rw-rwd","CP0"),VarSetCapacity(v,sz:=RawObjectSize(var,mode)+8,0)
	,RawObject(var,NumPut(sz-8,0+(ptr:=&v),"Int64"),mode),count:=sz//65536
	Loop % count
	  f.RawWrite(ptr+0,65536),ptr+=65536
	return sz,f.RawWrite(ptr+0,Mod(sz,65536)),f.Close()
  } else if !IsByRef(var)
		return RawObjectSize(obj,mode)+8
  else return sz,VarSetCapacity(var,sz:=RawObjectSize(obj,mode)+8,0),RawObject(obj,NumPut(sz-8,&var,"Int64"),mode)
}
RawObject(obj,addr,buf:=0,objects:=0){
  ; Type.Enum:    Char.1 UChar.2 Short.3 UShort.4 Int.5 UInt.6 Int64.7 UInt64.8 Double.9 String.10 Object.11
  ; Negative for keys and positive for values
  if !objects
	objects:={(""):0,(obj):0}
  else objects[obj]:=(++objects[""])
  for k,v in obj
  { ; 9 = Int64 for size and Char for type
	If !(kIsString:=0)&&IsObject(k){
	  If objects.HasKey(k)
		NumPut(-12,addr+0,"Char"),NumPut(objects[k],addr+1,"Int64"),addr+=9
	  else NumPut(-11,addr+0,"Char"),NumPut(sz:=RawObjectSize(k,buf),addr+1,"Int64"),RawObject(k,addr+9,buf,objects),addr+=sz+9
	}else if (k+0=""||k ""!=k+0||k~="\s")
	  kIsString:=true,NumPut(-10,addr+0,"Char"),NumPut(sz:=StrPut(k,addr+9)*2,addr+1,"Int64"),addr+=sz+9
	else NumPut( InStr(k,".")?-9:k>4294967295?-8:k>65535?-6:k>255?-4:k>-1?-2:k>-129?-1:k>-32769?-3:k>-2147483649?-5:-7,addr+0,"Char")
		,NumPut(k,addr+1,InStr(k,".")?"Double":k>4294967295?"UInt64":k>65535?"UInt":k>255?"UShort":k>-1?"UChar":k>-129?"Char":k>-32769?"Short":k>-2147483649?"Int":"Int64")
		,addr+=InStr(k,".")||k>4294967295?9:k>65535?5:k>255?3:k>-129?2:k>-32769?3:k>-2147483649?5:9
	If IsObject(v){
	  if objects.HasKey(v)
		NumPut( 12,addr+0,"Char"),NumPut(objects[v],addr+1,"Int64"),addr+=9
	  else NumPut( 11,addr+0,"Char"),NumPut(sz:=RawObjectSize(v,buf),addr+1,"Int64"),RawObject(v,addr+9,buf,objects),addr+=sz+9
	}else if (v+0=""||v ""!=v+0||v~="\s")
	  NumPut( 10,addr+0,"Char"),NumPut(sz:=buf?obj.GetCapacity(kIsString?"" k:k):StrPut(v)*2,addr+1,"Int64"),DllCall("RtlMoveMemory","PTR",addr+9,"PTR",buf?obj.GetAddress(kIsString?"" k:k):&v,"PTR",sz),addr+=sz+9
	else NumPut(InStr(v,".")?9:v>4294967295?8:v>65535?6:v>255?4:v>-1?2:v>-129?1:v>-32769?3:v>-2147483649?5:7,addr+0,"Char")
		,NumPut(v,addr+1,InStr(v,".")?"Double":v>4294967295?"UInt64":v>65535?"UInt":v>255?"UShort":v>-1?"UChar":v>-129?"Char":v>-32769?"Short":v>-2147483649?"Int":"Int64")
		,addr+=InStr(v,".")||v>4294967295?9:v>65535?5:v>255?3:v>-129?2:v>-32769?3:v>-2147483649?5:9
  }
}
RawObjectSize(obj,buf:=0,objects:=0){
  if !objects
	objects:={(obj):1}
  else if !objects.HasKey(obj)
	objects[obj]:=1
  sz:=0
  for k,v in obj
  {
	If !(kIsString:=0)&&IsObject(k)
	  sz+=objects.HasKey(k)?9:RawObjectSize(k,buf,objects)+9
	else if (k+0=""||k ""!=k+0||k~="\s")
	  kIsString:=true,sz+=StrPut(k)*2+9
	else sz+=InStr(k,".")||k>4294967295?9:k>65535?5:k>255?3:k>-129?2:k>-32769?3:k>-2147483649?5:9
	If IsObject(v)
	  sz+=objects.HasKey(v)?9:RawObjectSize(v,buf,objects)+9
	else if (v+0=""||v ""!=v+0||v~="\s")
	  sz+=(buf?obj.GetCapacity(kIsString?"" k:k):StrPut(v)*2)+9
	else sz+=InStr(v,".")||v>4294967295?9:v>65535?5:v>255?3:v>-129?2:v>-32769?3:v>-2147483649?5:9
  }
  return sz
}
User avatar
Chunjee
Posts: 1397
Joined: 18 Apr 2014, 19:05
Contact:

Re: help with uniq() for keyed values

24 Dec 2018, 09:46

TAC109 wrote:
23 Dec 2018, 22:49
Although the 6th and 7th entries in your testarray contain arrays which contains the same keys and values, this is by coincidence only; AHK will have created separate arrays. To make them the same, code as follows (untested):-
I tested your idea:

Code: Select all

Test1 := {"hello":"friend"}
Test2 := {"hello":"friend"}
TestArray := ["a","a","a","b","b",Test1,Test2]
uniq(TestArray)
;Actual => ["a","b",{"hello":"friend"},{"hello":"friend"}]
No dice.


I also tested what swagfag posted. It returned an array with only two elements: ["a","b"]
just me
Posts: 9406
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: help with uniq() for keyed values

24 Dec 2018, 09:59

Chunjee wrote:
24 Dec 2018, 09:46
I tested your idea: ...
You didn't! The idea is

Code: Select all

Test1:={"hello":"friend"}
TestArray := ["a","a","a","b","b",Test1,Test1] ; this will use the same object Test1 twice, so entries 6 and 7 are equal.
uniq(TestArray)
In your example, Test1 and Test2 are different objects. If you want to check whether they have the same contents, you have to check it.
User avatar
Chunjee
Posts: 1397
Joined: 18 Apr 2014, 19:05
Contact:

Re: help with uniq() for keyed values

24 Dec 2018, 10:19

fair enough. I guess it works differently than I was expecting, I'll try it in my app and see what happens. But my other test implies that it will not:

Code: Select all

TestArray[1,"hello"] := "friend"
TestArray[1,"test"] := "sample"
TestArray[2,"hello"] := "friend"
TestArray[2,"test"] := "sample"
results in a two item array.
User avatar
Chunjee
Posts: 1397
Joined: 18 Apr 2014, 19:05
Contact:

Re: help with uniq() for keyed values

26 Aug 2019, 07:43

I'm trying to get this working with key/value matching pairs again. Tested swagfag's function again and the output is ["a","b"]

Not good at all.
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: help with uniq() for keyed values

26 Aug 2019, 09:04

post full code of what ure testing and how ure testing it, cuz ur results dont make a lick of sense
the function identifies duplicate(by contents) objects and returns an object stripped of the duplicates
User avatar
Chunjee
Posts: 1397
Joined: 18 Apr 2014, 19:05
Contact:

Re: help with uniq() for keyed values

26 Aug 2019, 09:18

The input has been the same from the start of the thread: ["a","a","a","b","b",{"hello":"friend"},{"hello":"friend"}]

So it looks like this:

Code: Select all

TestArray := ["a","a","a","b","b",{"hello":"friend"},{"hello":"friend"}]
UniqArray := uniq(TestArray)
msgbox, % UniqArray.Length()
;; => Expected: 3
;; => Actual: 2
Last edited by Chunjee on 26 Aug 2019, 09:24, edited 1 time in total.
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: help with uniq() for keyed values

26 Aug 2019, 09:23

Image

Code: Select all

TestArray := ["a","a","a","b","b",{"hello":"friend"},{"hello":"friend"}]
newArr := uniq(TestArray)
Pause

uniq(arr) { ; Hash O(n)

	hash := {}
	newArr := []
	CachedObjects := []

	for each, element in arr
	{
		if IsObject(element)
		{
			size := ObjDump(element, dumped)

			if (CachedObjects.Count() == 0)
			{
				CachedObjects.Push({"ptr_data": &dumped, "size": size})
				newArr.Push(element)
			}
			else
			{
				for each, Cached in CachedObjects
				{
					if DllCall("msvcrt\memcmp", "Ptr", &dumped, "Ptr", Cached.ptr_data, "Int", Cached.size)
					{
						CachedObjects.Push({"ptr_data": &dumped, "size": size})
						newArr.Push(element)
					}
				}
			}
		}
		else if !(hash[element])
		{
			hash[element] := 1
			newArr.Push(element)
		}
	}
	return newArr
}

ObjDump(obj,ByRef var:="",mode:=0){
  If IsObject(var){ ; FileAppend mode
	If FileExist(obj){
	  FileDelete,%obj%
	  If ErrorLevel
		return
	}
	f:=FileOpen(obj,"rw-rwd","CP0"),VarSetCapacity(v,sz:=RawObjectSize(var,mode)+8,0)
	,RawObject(var,NumPut(sz-8,0+(ptr:=&v),"Int64"),mode),count:=sz//65536
	Loop % count
	  f.RawWrite(ptr+0,65536),ptr+=65536
	return sz,f.RawWrite(ptr+0,Mod(sz,65536)),f.Close()
  } else if !IsByRef(var)
		return RawObjectSize(obj,mode)+8
  else return sz,VarSetCapacity(var,sz:=RawObjectSize(obj,mode)+8,0),RawObject(obj,NumPut(sz-8,&var,"Int64"),mode)
}
RawObject(obj,addr,buf:=0,objects:=0){
  ; Type.Enum:    Char.1 UChar.2 Short.3 UShort.4 Int.5 UInt.6 Int64.7 UInt64.8 Double.9 String.10 Object.11
  ; Negative for keys and positive for values
  if !objects
	objects:={(""):0,(obj):0}
  else objects[obj]:=(++objects[""])
  for k,v in obj
  { ; 9 = Int64 for size and Char for type
	If !(kIsString:=0)&&IsObject(k){
	  If objects.HasKey(k)
		NumPut(-12,addr+0,"Char"),NumPut(objects[k],addr+1,"Int64"),addr+=9
	  else NumPut(-11,addr+0,"Char"),NumPut(sz:=RawObjectSize(k,buf),addr+1,"Int64"),RawObject(k,addr+9,buf,objects),addr+=sz+9
	}else if (k+0=""||k ""!=k+0||k~="\s")
	  kIsString:=true,NumPut(-10,addr+0,"Char"),NumPut(sz:=StrPut(k,addr+9)*2,addr+1,"Int64"),addr+=sz+9
	else NumPut( InStr(k,".")?-9:k>4294967295?-8:k>65535?-6:k>255?-4:k>-1?-2:k>-129?-1:k>-32769?-3:k>-2147483649?-5:-7,addr+0,"Char")
		,NumPut(k,addr+1,InStr(k,".")?"Double":k>4294967295?"UInt64":k>65535?"UInt":k>255?"UShort":k>-1?"UChar":k>-129?"Char":k>-32769?"Short":k>-2147483649?"Int":"Int64")
		,addr+=InStr(k,".")||k>4294967295?9:k>65535?5:k>255?3:k>-129?2:k>-32769?3:k>-2147483649?5:9
	If IsObject(v){
	  if objects.HasKey(v)
		NumPut( 12,addr+0,"Char"),NumPut(objects[v],addr+1,"Int64"),addr+=9
	  else NumPut( 11,addr+0,"Char"),NumPut(sz:=RawObjectSize(v,buf),addr+1,"Int64"),RawObject(v,addr+9,buf,objects),addr+=sz+9
	}else if (v+0=""||v ""!=v+0||v~="\s")
	  NumPut( 10,addr+0,"Char"),NumPut(sz:=buf?obj.GetCapacity(kIsString?"" k:k):StrPut(v)*2,addr+1,"Int64"),DllCall("RtlMoveMemory","PTR",addr+9,"PTR",buf?obj.GetAddress(kIsString?"" k:k):&v,"PTR",sz),addr+=sz+9
	else NumPut(InStr(v,".")?9:v>4294967295?8:v>65535?6:v>255?4:v>-1?2:v>-129?1:v>-32769?3:v>-2147483649?5:7,addr+0,"Char")
		,NumPut(v,addr+1,InStr(v,".")?"Double":v>4294967295?"UInt64":v>65535?"UInt":v>255?"UShort":v>-1?"UChar":v>-129?"Char":v>-32769?"Short":v>-2147483649?"Int":"Int64")
		,addr+=InStr(v,".")||v>4294967295?9:v>65535?5:v>255?3:v>-129?2:v>-32769?3:v>-2147483649?5:9
  }
}
RawObjectSize(obj,buf:=0,objects:=0){
  if !objects
	objects:={(obj):1}
  else if !objects.HasKey(obj)
	objects[obj]:=1
  sz:=0
  for k,v in obj
  {
	If !(kIsString:=0)&&IsObject(k)
	  sz+=objects.HasKey(k)?9:RawObjectSize(k,buf,objects)+9
	else if (k+0=""||k ""!=k+0||k~="\s")
	  kIsString:=true,sz+=StrPut(k)*2+9
	else sz+=InStr(k,".")||k>4294967295?9:k>65535?5:k>255?3:k>-129?2:k>-32769?3:k>-2147483649?5:9
	If IsObject(v)
	  sz+=objects.HasKey(v)?9:RawObjectSize(v,buf,objects)+9
	else if (v+0=""||v ""!=v+0||v~="\s")
	  sz+=(buf?obj.GetCapacity(kIsString?"" k:k):StrPut(v)*2)+9
	else sz+=InStr(v,".")||v>4294967295?9:v>65535?5:v>255?3:v>-129?2:v>-32769?3:v>-2147483649?5:9
  }
  return sz
}
User avatar
Chunjee
Posts: 1397
Joined: 18 Apr 2014, 19:05
Contact:

Re: help with uniq() for keyed values

26 Aug 2019, 09:30

I updated from 1.1.27 to 1.1.30 and now the length is 3. That's kinda insane but ok
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: help with uniq() for keyed values

26 Aug 2019, 10:09

not at all. .Count() is a 1.1.29 method
User avatar
Chunjee
Posts: 1397
Joined: 18 Apr 2014, 19:05
Contact:

Re: help with uniq() for keyed values  Topic is solved

26 Aug 2019, 10:37

Code: Select all

uniq(para_collection) {
    global
    if (!IsObject(para_collection)) {
        return false
    }
    dummy_Array := []
    info_Array := []
    Loop, % para_collection.MaxIndex() {
        printedelement := MD5(Array_Print(para_collection[A_Index]))
        if (indexOf(dummy_Array,printedelement) == -1) {
            dummy_Array.push(printedelement)
            info_Array.push(para_collection[A_Index])
        }
    }
    return info_Array
}
:think:

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Google [Bot] and 189 guests