Friday, January 28, 2022

#1 2021-07-09 06:29:05 am

Fredrik71
Member
Registered: 2019-10-23
Posts: 929

Key-Value Coding Programming: valueForKeyPath

I'm doing some reference to understand (KVC) Key-Value Coding Programming with valueForKeyPath method. Here is some example of that using valueForKeyPath and if I understand this correct from NSKeyValueOperator Its a array operator. The examples of anArray variable with objects and values.

I thought I could take keys and values from NSDictionary to become objects in NSArray like
the anArray variable. I did this with AS but it complain about not be KVC compatible array.

Is this possible and  also be KVC compatible ??

Or have I miss something...

Applescript:

use framework "Foundation"
use scripting additions

(**
* Key-Value Coding Programming Guide
* Reference:
*    https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/KeyValueCoding/index.html
*    https://developer.apple.com/documentation/foundation/nskeyvalueoperator?language=objc
*)


(**
* [Instance Method]: valueForKeyPath:
*    Returns the value for the derived property identified by a given key path.
**
* A collection operator is one of a small list of keywords preceded by an at sign (@)
* that specifies an operation that the getter should perform to manipulate the data
* in some way before returning it.
*)

set anNewArray to {abc:1000, def:250}
set anArray to {{a:1, e:250, c:3}, {b:250, e:150, c:40}, {b:5, d:7}, {e:100}, {b:5}, {c:10, d:15}, {c:55}, {e:100, f:200}, anNewArray}
set arrayWithValuesAndKeys to (current application's NSMutableArray's arrayWithArray:anArray)

set average to (arrayWithValuesAndKeys's valueForKeyPath:"@avg.b") as integer
log average

set countKeys to (arrayWithValuesAndKeys's valueForKeyPath:"@count") as integer
log countKeys

set maxValue to (arrayWithValuesAndKeys's valueForKeyPath:"@max.c") as integer
log maxValue

set minValue to (arrayWithValuesAndKeys's valueForKeyPath:"@min.d") as integer
log minValue

set sumValue to (arrayWithValuesAndKeys's valueForKeyPath:"@sum.e") as integer
log sumValue

set uniqueKeys to arrayWithValuesAndKeys's valueForKeyPath:"@distinctUnionOfObjects.@allKeys"
log uniqueKeys as list

set uniqueValues to arrayWithValuesAndKeys's valueForKeyPath:"@distinctUnionOfObjects.@allValues"
log uniqueValues as list

set uniqueCollectionValues to arrayWithValuesAndKeys's valueForKeyPath:"@distinctUnionOfObjects.e"
log uniqueCollectionValues as list

set collectionValues to arrayWithValuesAndKeys's valueForKeyPath:"@unionOfObjects.c"
log collectionValues as list

set uniqueKeys to arrayWithValuesAndKeys's valueForKeyPath:"@distinctUnionOfArrays.@allKeys"
log uniqueKeys as list

set uniqueValues to arrayWithValuesAndKeys's valueForKeyPath:"@distinctUnionOfArrays.@allValues"
log uniqueValues as list

set uniqueValues to arrayWithValuesAndKeys's valueForKeyPath:"@distinctUnionOfObjects.abc"
log uniqueValues as list

-- We add a second array so we need to use firstObject()
arrayWithValuesAndKeys's setValue:100 forKey:"abc"
log (arrayWithValuesAndKeys's valueForKeyPath:"@unionOfObjects.abc")'s firstObject() as list

arrayWithValuesAndKeys's setValue:true forKey:"abc"
log arrayWithValuesAndKeys as list
-- Here we could see that bool return NSNumber in this case 1 for true
log (arrayWithValuesAndKeys's valueForKeyPath:"abc")'s firstObject() as integer
log (arrayWithValuesAndKeys's valueForKeyPath:"abc")'s firstObject()'s superclass()'s |description|() as text


if you are the expert, who will you call if its not your imagination.


Filed under: KVC, valueForKeyPath

Offline

 

#2 2021-07-09 07:08:28 am

Fredrik71
Member
Registered: 2019-10-23
Posts: 929

Re: Key-Value Coding Programming: valueForKeyPath

I got something working but not sure if this is the best approach.
The example use 2 keys with same name with different values and return the max value.

Applescript:

set {theObjects, theKeys} to {{10, 200, 20}, {"a", "b", "a"}}
set theDict to current application's NSMutableDictionary's dictionaryWithObjects:theObjects forKeys:theKeys
log (theDict's valueForKey:"a") as integer
log theDict as record
set valuesAndKeys to current application's NSMutableArray's arrayWithArray:(theDict as list)
set theMax to (valuesAndKeys's valueForKeyPath:"@max.a") as integer
log "Max of a: " & theMax

the problem is... valueForKeyPath think its 3 keys this is not correct... its will take the value of the last key name so it will do the same as AS convert the same key name to be only 1 instance. In other words the example only have keys a, b and the value of a is 30.
On other hand if we ask theDict for allValues we will get 20, 200... I guess every object of instance of key value pair could only use unique key name. That said... valueForKeyPath could still be used
if that was the case and to return array operator of key name with same name.

More test its looks like @max return the last key name values and that doesn't have to be the maximum value. So I guess the point of this code break everything... back to research more smile

Sorry if this is confusing... but I really like to understand valueForKeyPath and how I could approach it to be used in ASObjC. I do know the sometimes we need valueForKeyPath instead of
valueForKey. And to do have some examples of a NSArray or NSDictionary to demonstrate it would be a good thing, smile

Last edited by Fredrik71 (2021-07-09 08:07:54 am)


if you are the expert, who will you call if its not your imagination.

Offline

 

#3 2021-07-09 08:04:06 am

Nigel Garvey
Moderator
From:: Warwickshire, England
Registered: 2002-11-20
Posts: 5475

Re: Key-Value Coding Programming: valueForKeyPath

Hi Fredrick.

Your first script's running without complaint for me in Mojave. But the results aren't always what seems to be expected. For instance:

Applescript:

set average to (arrayWithValuesAndKeys's valueForKeyPath:"@avg.b") as real -- Modified from 'as integer'.
return average
--> 28.888888888889

The result's the sum of all the 'b's divided by the number of dictionaries in the array, not by the number of dictionaries with 'b's.


NG

Offline

 

#4 2021-07-09 08:16:51 am

Fredrik71
Member
Registered: 2019-10-23
Posts: 929

Re: Key-Value Coding Programming: valueForKeyPath

Hi Nigel,

I get the same on my machine: Mojave...

Do you have any example how to use valueForKeyPath compare to valueForKey ??

I doing some research on KVC to understand it better.

Thanks.


if you are the expert, who will you call if its not your imagination.

Offline

 

#5 2021-07-10 04:15:06 am

Nigel Garvey
Moderator
From:: Warwickshire, England
Registered: 2002-11-20
Posts: 5475

Re: Key-Value Coding Programming: valueForKeyPath

Fredrik71 wrote:

Do you have any example how to use valueForKeyPath compare to valueForKey ??


Hi Fredrik.

My understanding is that, whereas valueForKey: might return the value of a particular property of an object (if the property key's a string), or an array of all such values from an array of such objects, valueForKeyPath: does the same by following a chain of keys. This makes it good for referencing nested properties. It can be thought of as a succession of valueForKey: actions. Or valueForKey: can be thought of as a special case of it.

Applescript:

use AppleScript version "2.4" -- OS X 10.10 (Yosemite) or later
use framework "Foundation"

-- Flat dictionary:
set aRecord to {a:1, b:2, c:3}
set aDictionary to current application's NSDictionary's dictionaryWithDictionary:(aRecord)
(aDictionary's valueForKey:("a")) as item --> 1
(aDictionary's valueForKeyPath:("a")) as item --> 1

-- Array thereof:
set aList to {{a:1, b:2, c:3}, {a:4, b:5, c:6}, {a:7, b:8, c:9}}
set anArray to current application's NSArray's arrayWithArray:(aList)
(anArray's valueForKey:("a")) as item --> {1, 4, 7}
(anArray's valueForKeyPath:("a")) as item --> {1, 4, 7}

-- Nested dictionary:
set aRecord to {a:1, b:2, c:{d:"aardvark", e:"banana"}}
set aDictionary to current application's NSDictionary's dictionaryWithDictionary:(aRecord)
((aDictionary's valueForKey:("c"))'s valueForKey:("e")) as item --> "banana"
(aDictionary's valueForKeyPath:("c.e")) as item --> "banana"

-- Array thereof:
set aList to {{a:1, b:2, c:{d:"aardvark", e:"banana"}}, {a:4, b:5, c:{d:"cat", e:"dog"}}, {a:7, b:8, c:{d:"elephant", e:"fish"}}}
set anArray to current application's NSArray's arrayWithArray:(aList)
((anArray's valueForKey:("c"))'s valueForKey:("e")) as item --> {"banana", "dog", "fish"}
(anArray's valueForKeyPath:("c.e")) as item --> {"banana", "dog", "fish"}

-- Where an object doesn't have the specified property, the corresponding result is NULL (or missing value in AS):
set aList to {{a:1, b:2, c:{d:"aardvark", e:"banana"}}, {a:4, b:5, x:{y:"cat", z:"dog"}}, {a:7, b:8, c:{d:"elephant", e:"fish"}}}
set anArray to current application's NSArray's arrayWithArray:(aList)
(anArray's valueForKeyPath:("c.e")) as item --> {"banana", missing value, "fish"}

Collection operators only make sense (if at all!  wink) where valueForKeyPath: is applied to an array. Shane's book only shows them used with the "self" key, where they're applied directly to the values in the array. But your scripts show that they can also be used with other keys to access particular properties. However, you have to be prepared for consequences if not all the objects have the specified properties, the operators have to be relevant to the values, and the keys have to be strings.

I don't think I've seen the "@allKeys" and "@allValues" operators before, let alone two operators in the same key path. Interesting.

PS. The keys are strings in dictionaries derived from AppleScript records. It's only on the ObjectiveC side of things that other objects can be used as keys.


NG

Offline

 

#6 2021-07-10 06:27:11 am

Fredrik71
Member
Registered: 2019-10-23
Posts: 929

Re: Key-Value Coding Programming: valueForKeyPath

Thanks Nigel, very useful information.

@allKeys and @allValues...

not only that... you could also use:

@unionOfArrays.@objectEnumerator -- same as allValues
@unionOfArrays.@allObjects -- same as allValues


if you are the expert, who will you call if its not your imagination.

Offline

 

Board footer

Powered by FluxBB

RSS (new topics) RSS (active topics)