Accessing list of records

Hi all together,

I try to get from one list of records a subset which is fitting to a atribute like that:


set tableData to {{|name|:"Bart Simpson", city:"Springfield", zip:"19542", age:12}, ¬
	{|name|:"Ally McBiel", city:"Chicago", zip:"91544", age:28}, ¬
	{|name|:"Joan of Ark", city:"Paris", zip:"53255", age:36}, ¬
	{|name|:"King Tut", city:"Egypt", zip:"00245", age:45}, ¬
	{|name|:"James Taylor", city:"Atlanta", zip:"21769", age:42}}
set tableNewEntry to {{|name|:"King Nik", city:"Springfield", zip:"00100", age:33}}

set tableData to tableData & tableNewEntry

set sucheEintrag to (every record of tableData whose "city" is "Springfield") as record

-- the result what I am looking for should be that:
-- sucheEintrag = {	{|name|:"Bart Simpson", city:"Springfield", zip:"19542", age:12}, ¬
--				{|name|:"King Nik", city:"Springfield", zip:"00100", age:33}}


display dialog city of record 1 of tableData
display dialog city of record 1 of sucheEintrag


I hope one of You can help me, and say me where is my mistake, and what should I do in correct way…
Because it doesn’t work, how it should be. I don’t want to use a loop and to check every Element of the List,
because the List is in realy whery long, an can have over 10.000 Entries… :frowning:

Hi,

I think your only option is to loop throught the elements. Trying to make it as fast as possible:

set tableData to {{|name|:"Bart Simpson", city:"Springfield", zip:"19542", age:12}, ¬
	{|name|:"Ally McBiel", city:"Chicago", zip:"91544", age:28}, ¬
	{|name|:"Joan of Ark", city:"Paris", zip:"53255", age:36}, ¬
	{|name|:"King Tut", city:"Egypt", zip:"00245", age:45}, ¬
	{|name|:"James Taylor", city:"Atlanta", zip:"21769", age:42}}
set tableNewEntry to {{|name|:"King Nik", city:"Springfield", zip:"00100", age:33}}

set tableData to tableData & tableNewEntry

set tableDataRef to a reference to tableData -- supposed to speed up references in long lists
--set testThen to (current date) -- time tester (seconds)
set sucheEintrag to {}
repeat with myRecordReference in tableDataRef
	if city of myRecordReference is "Springfield" then copy (myRecordReference as record) to the end of sucheEintrag -- supposed to be faster than "set x to x & y"
end repeat
--display dialog ((current date) - testThen) -- time tester  (seconds)

display dialog city of record 1 of tableData
display dialog city of record 1 of sucheEintrag

See pages on “Reference To”, Lists and Records from the Apple Applescript Guide.

You can probably do this faster using a shell script.

Best wishes

John M

Hi, MrLite.

An expression like ‘every … whose …’ can only be used when scripting elements of certain applications. Since tableData is an AppleScript list - not an application reference - you have to use a repeat loop.

Also, since the result you want is a two-item list, you can’t coerce it to record. And you shouldn’t put “city” in quotes when accessing a property of that name in a record. :slight_smile:

set tableData to {{|name|:"Bart Simpson", city:"Springfield", zip:"19542", age:12}, ¬
  {|name|:"Ally McBiel", city:"Chicago", zip:"91544", age:28}, ¬
  {|name|:"Joan of Ark", city:"Paris", zip:"53255", age:36}, ¬
  {|name|:"King Tut", city:"Egypt", zip:"00245", age:45}, ¬
  {|name|:"James Taylor", city:"Atlanta", zip:"21769", age:42}}
set tableNewEntry to {{|name|:"King Nik", city:"Springfield", zip:"00100", age:33}}

set tableData to tableData & tableNewEntry

set sucheEintrag to {}
repeat with thisRecord in tableData
  if thisRecord's city is "Springfield" then
    set end of sucheEintrag to thisRecord's contents
  end if
end repeat

I will try it this way, but its not exactly what I needed, but if its realy the only way, I will go for it… :frowning:

But BIG THX for helping …

Mr Light :slight_smile:

huh I see, I miss some knolege, I searched the net, but didn’t find a nice way how to make a Record generely. Whithout to define it before, I want to read Properties of Songs from iTunes and to put them in a list of Records, to do nice things with it. I know how to comunicate with iTunes, but how to handle the Informations…?


-- which way is better?


property |name| : "King Nik"
property city : "Springfield"
property zip : "00100"
property age : 33

-- and then to use something like:

set property "city" in tableNewEntry to "New York" --> {city:"New York"}

-- or something like

ad property "city" in tableNewEntry to "New York" --> {city:"New York"}

-- or better something like that:

set |name| to "King Nik"
set city to "Springfield"
set zip to "00100"
set age to 33

-- and then something like that:

set tableNewEntry to {|name|, city, zip, age} as record

-- to get this:

set tableNewEntry to {{|name|:"King Nik", city:"Springfield", zip:"00100", age:33}}

-- but I got only this: {"King Nik", "Springfield", "00100", 33}

What should I do, to get the fastest way?

Please help me again :slight_smile:

THX

Mr Light :slight_smile:

Hi,

What abut?

set theName to "King Nik"
set theCity to "Springfield"
set theZip to "00100"
set theAge to 33

set tableNewEntry to {} -- empty list

copy {|name|:theName, city:theCity, zip:theZip, age:theAge} to the end of tableNewEntry
return tableNewEntry -- to show state of tableNewEntry in result pannel
-- returns
--{{|name|:"King Nik", city:"Springfield", zip:"00100", age:33}}

Best wishes

John M

Due to crappy design, looking up items in AppleScript lists normally takes linear time, i.e. time to set or get an item by index varies directly with the length of the list. You can hack around this by wrapping the list in a script object, allowing you to refer to it by reference which, due to additional - but fortuitous! - crappy design, gives you a constant lookup time instead. (The same trick also improves performance when appending items to a list, though this it’s still less efficient than it should be.)

on run
    script tableData
        property lst : {{|name|:"Bart Simpson", city:"Springfield", zip:"19542", age:12}, ¬
            {|name|:"Ally McBiel", city:"Chicago", zip:"91544", age:28}, ¬
            {|name|:"Joan of Ark", city:"Paris", zip:"53255", age:36}, ¬
            {|name|:"King Tut", city:"Egypt", zip:"00245", age:45}, ¬
            {|name|:"James Taylor", city:"Atlanta", zip:"21769", age:42}, ¬
            {|name|:"King Nik", city:"Springfield", zip:"00100", age:33}}
    end script
    
    repeat 11 times -- TEST: make a ~12,000 item list to stress-test with
        set tableData's lst to tableData's lst & tableData's lst
    end repeat


    script sucheEintrag
        property lst : {}
    end script
    
    repeat with thisRecord in tableData's lst
        if thisRecord's city is "Springfield" then
            set end of sucheEintrag's lst to thisRecord's contents
        end if
    end repeat
end run

This takes about a second to run OMM, which is still pretty crap, but at least it’s not going through the floor like the non-optimised version so might be within acceptable limits for your needs. Otherwise you should consider using an external database (FileMaker, MySQL, etc.) or other searchable data store (e.g. XMLLib.osax), which’ll rip through your 10,000 records in zero time, or switch to a a faster language - e.g. the equivalent vanilla code in Python (which isn’t particularly fast) runs 20 times faster than the AppleScript version above:

[code]#!/usr/bin/python

tableData = [
{“name”:“Bart Simpson”, “city”:“Springfield”, “zip”:“19542”, “age”:12},
{“name”:“Ally McBiel”, “city”:“Chicago”, “zip”:“91544”, “age”:28},
{“name”:“Joan of Ark”, “city”:“Paris”, “zip”:“53255”, “age”:36},
{“name”:“King Tut”, “city”:“Egypt”, “zip”:“00245”, “age”:45},
{“name”:“James Taylor”, “city”:“Atlanta”, “zip”:“21769”, “age”:42},
{“name”:“King Nik”, “city”:“Springfield”, “zip”:“00100”, “age”:33},
]

tableData = tableData * 2048 # TEST: make a ~12,000 item list to stress-test with

sucheEintrag = [item for item in tableData if item[“city”].lower() == “springfield”][/code]
HTH

has

@@All of You,

Big THX for the fast Help and nice Hints, I think, I will try it for now with the References (thx @John M), I think @hhas was right, and as second step I will try with my roudiment Knowlage in C to make the searching Procedure. I have only to fix 2 litle Problems before. A) how to comunikate between a C programm and my Script, an B) how to build the same Data Structure in C. But I am shure, there is lot of hints in the net…

THX all

Mr Light :slight_smile:

Actually it works fine, but I didn’t realy understand why. I have to say that I never saw before something like that

–>'s<–

Is it only a shorter way to write:

city of thisRecord

?

and second I didn’t understand this Part:


	script sucheEintrag
		property lst : {}
	end script

where is the diference to


	on sucheEintrag ()
		property lst : {}
	end sucheEintrag

and how You use this Script in


           set end of sucheEintrag's lst to thisRecord's contents

My Mac understand it well, but I didn’t… :slight_smile:

Gerts.

Mr Light

Be aware that the ‘a reference to’ operator doesn’t work with local variables, so John M’s example only works when tableData is declared as a property or global (top-level) variable. Use the ‘property wrapped in script object’ version if you’re keeping your list in local variables. (Gods, how I hate these kludges.)

I wouldn’t bother if I were you. While a basic osax command where you pass the entire list each time wouldn’t be hard to write, the overhead from packing a 10,000-item list into an Apple event is such that it’d probably only give you a 2 or 3x improvement over the optimised vanilla solution - really not worth it. The only way you’ll get significantly better performance over vanilla AS is if you keep your data on the other side the whole time, and if you’re going to do that it’ll be far less work to use an existing solution (FileMaker, MySQL, XMLLib, etc.) than develop your own ad-hoc database osax/app from scratch. Alternatively, just grit your teeth and console yourself with the thought that, slow as AppleScript is, it’s still way faster than doing everything by hand.

Hi,

It’s a hack to make the script run faster with long lists. Having seen this done a few times now I’m only just starting to get my head around it :confused: (hhas’s explanation in this topic has helped a lot in this :slight_smile: )

This is wrapping the list in a script object:

script sucheEintrag
       property lst : {}
   end script

(You’ll notice that hhas has done the same with the tableData variable.)
Then, when refering to the list in the main script you use stuff like “sucheEintrag’s lst”. Then the time to retrieve the data is constant (rather than taking more time for a longer list).

…or I could have the wrong end of the stick…

Best wishes

John M

Yes. “foo’s bar” is same as “bar of foo”.

The first defines a script object containing a single property named ‘lst’. The second defines a command/event handler, and is syntactically invalid since you can’t declare properties within handlers, only within script objects.

(Hint: your entire script is itself one big script object, just without the explicit ‘script…end script’ declaration. You only use ‘script…end script’ when defining a script object within another script object or handler.)

‘set end of someList to someValue’ appends the value stored in variable someValue to the list object stored in variable someList. (AppleScript lists are mutable and allow you to insert new items at beginning and end, as well as replace existing items.) Inserting values into an existing list is quicker and more efficient than creating a new list object by joining two existing ones via the concatenation (&) operator.

HTH

No, you’ve got it right. Using ‘item i of a reference to ’ has exactly the same effect as ‘item i of of ’; the extra step of indirection somehow causes AppleScript to bypass the extra internal logic that causes the drop in efficiency. The advantage of the latter is that you’re not forced to put your list(s) into global variables but can continue to keep them in local variables. Either way it’s a complete hack, of course, though even the ASLG mentions it so at least it’s a dependable one. :stuck_out_tongue:

It’s a real PITA for ASers to have to deal with crud like this, mind you, especially in what’s supposed to be a ‘novice-friendly’ and ‘easy-to-use’ language. And rather ridiculous considering other languages implement equivalent list structures without any such problems. But hey, that’s AppleScript for you; either you learn to live with its little… ahh, “eccentricities”, or you run screaming for the hills. ;p