Checking for Unique Elements in a List()

Just starting out? Need help? Post your questions and find answers here.
User avatar
blueb
Addict
Addict
Posts: 1116
Joined: Sat Apr 26, 2003 2:15 pm
Location: Cuernavaca, Mexico

Checking for Unique Elements in a List()

Post by blueb »

The code below shows what I'm up to... but it seems like a convoluted way to check that the 'Primary Key' is unique.
Perhaps someone has a better way to have a 'Uniqueness' check when adding new items to Lists() :?:

Code: Select all

;Test

Structure TestStructure
  RowID.s
  Name.s
EndStructure

NewList mylist.TestStructure()

AddElement(mylist())
mylist()\RowID = "1"
mylist()\Name = "Anna"

AddElement(mylist())
mylist()\RowID = "2"
mylist()\Name = "Amy"

AddElement(mylist())
mylist()\RowID = "3"
mylist()\Name = "Ron"

AddElement(mylist())
mylist()\RowID = "4"
mylist()\Name = "Roger"
; ================================

;Add a new item, but check that the RowID is unique... 
; Same as a 'Primary Key' in SQLite

InsertNewRow.s = "2" ; RowID I want to add.

ResetList(mylist())        ; start at the top of the List()

While NextElement(mylist()); 
  If mylist()\RowID = InsertNewRow
     MessageRequester("", "'RowID' is not unique... abandoned the insert")
     Break ; returns the last elements information.
 Else 
    AddElement(mylist())
    mylist()\RowID = InsertNewRow
    mylist()\Name = "'Blueb' added"
 EndIf
 
Wend

Debug "At position "+ ListIndex(mylist()) +", the RowID is: "+ mylist()\RowID + " the name is: " + mylist()\Name
- It was too lonely at the top.

System : PB 6.21(x64) and Win 11 Pro (x64)
Hardware: AMD Ryzen 9 5900X w/64 gigs Ram, AMD RX 6950 XT Graphics w/16gigs Mem
User avatar
skywalk
Addict
Addict
Posts: 4224
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: Checking for Unique Elements in a List()

Post by skywalk »

You could use a :memory: sqlite table and get this for free.
Or populate a Map() with your rowid. The Map() only allows 1 unique entry.
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
SMaag
Enthusiast
Enthusiast
Posts: 325
Joined: Sat Jan 14, 2023 6:55 pm
Location: Bavaria/Germany

Re: Checking for Unique Elements in a List()

Post by SMaag »

Use a Map() and a List() then you can use the Map() to index the List.

I guess there is anywehre in the PB Help a description for that. I remember it but at the moment I could not find it!
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: Checking for Unique Elements in a List()

Post by netmaestro »

You could use an index for the list that would consist of a Map() that would contain all current row IDs and nothing else.

Code: Select all

NewMap keys.i()

x = 1
keys(Str(x)) = 0 ; Do this before adding the element to the list

If FindMapElement(keys(), Str(x))
  Debug "Key "+Str(x) +" exists"
Else
  Debug "Key "+Str(x) +" is not found in the index" ; If you land here you know the key is unique
EndIf
Your map index will be compact and lightning fast.
Last edited by netmaestro on Wed Jul 10, 2024 4:08 pm, edited 2 times in total.
BERESHEIT
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4955
Joined: Sun Apr 12, 2009 6:27 am

Re: Checking for Unique Elements in a List()

Post by RASHAD »

Hi

Code: Select all

ForEach mylist()
    If mylist()\RowID = InsertNewRow
      Debug "exist"
    EndIf
Next
Egypt my love
User avatar
Demivec
Addict
Addict
Posts: 4270
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: Checking for Unique Elements in a List()

Post by Demivec »

Here's a way using the map idea:

Code: Select all

;Test
EnableExplicit

Structure TestStructure
  RowID.s
  Name.s
EndStructure

NewList mylist.TestStructure()
NewMap mylist_primaryKey()
#rowID_padsize = 5 ;how many digits in rowID including leading zeroes.

;Return pointer to existing element if RowID already exists or 0.
;If moveToExistingElement <> 0 then change current list element to existing element. 
Procedure.i elementExists(List mylist.TestStructure(), Map mylist_primaryKey(), rowID$, moveToExistingElement = #False)
  If FindMapElement(mylist_primaryKey(), rowID$) <> 0
    If moveToExistingElement
      ChangeCurrentElement(mylist(), mylist_primaryKey())
    EndIf
    ProcedureReturn mylist_primaryKey() ;RowID already exists
  Else
    ProcedureReturn  0 ;RowID does not exist
  EndIf
EndProcedure 

;Adds and returns pointer to new list element
Procedure.i addNewElement(List mylist.TestStructure(), Map mylist_primaryKey(), rowID$, name$)
  LastElement(mylist())
  AddElement(mylist())
  mylist()\RowID = rowID$
  mylist()\Name = name$
  
  mylist_primaryKey(rowID$) = @mylist()
  ProcedureReturn mylist_primaryKey()
EndProcedure 

;Remove list element with rowID and update primary key map
Procedure.i removeElement(List mylist.TestStructure(), Map mylist_primaryKey(), rowID$)
  If elementExists(mylist(), mylist_primaryKey(), rowID$, 1)
    DeleteMapElement(mylist_primaryKey())
    DeleteElement(mylist())
    ProcedureReturn #True ; element found and deleted
  EndIf
  
  ProcedureReturn #False ;no element deleted
EndProcedure 

Define i, InsertNewRow.s,
       nameGen_start.s="Anne,Ash,Bea,Beau,Bekke,Blaine,Blaire,Blake,Blue,Bree,Brooke,Bryce,Bryne,Cade,Cain,Chad,Chase,Claire," +
                       "Clay,Cole,Craig,Dean,Drake,Eve,Faith,Fern,Flynn,Gage,Grant,Greer,Guy,Hayes,Heath,Hope,Hugh,Jace,Jack," +
                       "Jade,Jai,Jake,James,Jay,Joel,John,Joy,Jude,June,Kai,Kate,Kent,Kurt,Kyle,Lane,Lark,Leif,Liv,Love,Luke," +
                       "Lynn,Mark,Max,May,Neve,Noor,Paige,Park,Paul,Pax,Pearl,Quinn,Rae,Rain,Reese,Reid,Rex,Rose,Rue,Ruth,Sage," +
                       "Sam,Seth,Shae,Shane,Skye,Sloane,Snow,Starr,Taj,Tate,Tess,Trace,Troy,True,Vale,Vance,Viv,Wren,Wyn,Zack",
       nameGen_start2.s="A,Aar,Aid,Al,An,Au,Bel,Bran,Bry,Ca,Car,Char,Chlo,Clar,Con,Coop,Cor,Dan,Dav,Dyl,E,Ed,El,Em,Ev,Gar,Hai," +
                        "Han,Harp,Haz,Hen,Hud,Hun,I,Is,Ja,Jack,Jo,Jor,Kay,Lan,Lau,Le,Li,Lil,Lo,Log,Lu,Ma,Mag,Mar,Mat,Me,Mi,Mol," +
                        "Na,Ni,No,Nor,O,Ol,Or,Pa,Pai,Par,Pey,Pi,Rea,Ri,Ro,Ru,Ry,Sa,Scar,Sky,So,Stel,Tay,Thom,Ty,Wil,Wy,Zo,",
       nameGen_end.s="ea,ah,am,an,as,att,belle,bert,brey,by,cas,chael,chid,cob,cole,dam,dan,den,die,dith,don,dra,drew,e,el," +
                     "elle,en,er,ery,ett,ey,gan,gie,gio,gory,ham,hew,ice,ick,id,ie,iel,ive,ker,la,lah,lar,las,leb,lei,ler," +
                     "let,ley,liam,lian,lo,lor,lotte,low,ly,lyn,ma,min,na,nah,nic,niel,nor,ny,on,ot,per,pher,phie,rah,ren," +
                     "rett,rine,ry,sa,saac,seph,sie,sley,son,stin,ter,tha,than,ton,trick,tumn,va,vi,vieve,wen,y,ya,zie", 
       newName.s

;Add a few names
For i = 1 To 100
  InsertNewRow.s = RSet(Str(i), #rowID_padsize, "0")
  If Random(2) > 1
    newName.s = StringField(nameGen_start, Random(CountString(nameGen_start, ",")) + 1, ",")
  Else
    newName.s = StringField(nameGen_start2, Random(CountString(nameGen_start2, ",")) + 1, ",") +
                StringField(nameGen_end, Random(CountString(nameGen_end, ",")) + 1, ",")
    If Random(1)
      newName + StringField(nameGen_end, Random(CountString(nameGen_end, ",")) + 1, ",")
    EndIf
  EndIf
  
  addNewElement(mylist(), mylist_primaryKey(), InsertNewRow, newName)
Next

SortStructuredList(mylist(), #PB_Sort_Ascending, OffsetOf(TestStructure\RowID), #PB_String)
Debug "**** List in row order"
ForEach mylist()
  Debug mylist()\RowID + ": " + mylist()\Name
Next

SortStructuredList(mylist(), #PB_Sort_Ascending, OffsetOf(TestStructure\Name), #PB_String)
Debug "**** List in name order"
ForEach mylist()
  Debug mylist()\RowID + ": " + mylist()\Name
Next

;delete a few rows
Define deleteRow.s, rowsDeleted = 0
For i = 1 To 20
  deleteRow = RSet(Str(Random(100)), #rowID_padsize, "0")
  If removeElement(mylist(), mylist_primaryKey(), deleteRow)
    rowsDeleted + 1
    Debug "'RowID " + deleteRow + "' has been removed."
  Else
    Debug "'RowID " + deleteRow + "' does not exist, abandoned the removal."
  EndIf
Next
Debug "**** Total of " + rowsDeleted + " row(s) were removed."

; ================================

;Add a new item, but check that the RowID is unique... 
; Same as a 'Primary Key' in SQLite
Define foundUniqueRowID = #False
Repeat
  InsertNewRow.s = RSet(Str(Random(MapSize(mylist_primaryKey()) + 1, 1)), #rowID_padsize, "0") ; RowID I want to add.
  If elementExists(mylist(), mylist_primaryKey (), InsertNewRow, 1) <> 0
    ;MessageRequester("", "'RowID' is not unique... abandoned the insert")
    Debug "'RowID " + InsertNewRow + "' is not unique... abandoned the insert"
    ;returns the last elements information
  Else
    foundUniqueRowID = #True
    addNewElement(mylist(), mylist_primaryKey(), InsertNewRow, "'Blueb' added")
  EndIf
  
  
  Debug "At position "+ ListIndex(mylist()) +", the RowID is: "+ mylist()\RowID + " the name is: " + mylist()\Name
Until foundUniqueRowID

SortStructuredList(mylist(), #PB_Sort_Ascending, OffsetOf(TestStructure\RowID), #PB_String)
Debug "**** List in row order"
ForEach mylist()
  Debug mylist()\RowID + ": " + mylist()\Name
Next

SortStructuredList(mylist(), #PB_Sort_Ascending, OffsetOf(TestStructure\Name), #PB_String)
Debug "**** List in name order"
ForEach mylist()
  Debug mylist()\RowID + ": " + mylist()\Name
Next
@Edit: added an orderly way to remove an element also., RASHAD. :)
@Edit: edited to correct syntax.
@Edit: edited to make a much more complex test example including fake name generation. An exercise left for the reader is to add a way of tracking available RowIDs instead of the random approach used here or even a simple incrementing RowID.
Last edited by Demivec on Thu Jul 11, 2024 1:29 am, edited 3 times in total.
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4955
Joined: Sun Apr 12, 2009 6:27 am

Re: Checking for Unique Elements in a List()

Post by RASHAD »

Thanks Demivec
I don't want to be rude :D
Egypt my love
User avatar
blueb
Addict
Addict
Posts: 1116
Joined: Sat Apr 26, 2003 2:15 pm
Location: Cuernavaca, Mexico

Re: Checking for Unique Elements in a List()

Post by blueb »

Yes I could use SQLite, but I've been using Droopy's Little Database, with modifications. I'd like to stick to using Lists()... this what Droopy's uses, and it's pure PureBasic. Besides it works very well for smaller projects.

I thought I could add one or two more items to improve the module... one being adding 'Primary Keys'. What I was doing (see my first post), and RASHAD's idea seems to be the simplest method, so far. I may look into using a MAP() with 'Primary Keys', but it kind of goes against what the module is all about.

Thanks for the posts. Any other 'Link-List' versions?
- It was too lonely at the top.

System : PB 6.21(x64) and Win 11 Pro (x64)
Hardware: AMD Ryzen 9 5900X w/64 gigs Ram, AMD RX 6950 XT Graphics w/16gigs Mem
User avatar
NicTheQuick
Addict
Addict
Posts: 1523
Joined: Sun Jun 22, 2003 7:43 pm
Location: Germany, Saarbrücken
Contact:

Re: Checking for Unique Elements in a List()

Post by NicTheQuick »

There is no better way than iterating through the whole list to find the duplicate.
Even if the order of the list does not matter and you would sort it by its primary key, you would not be able to do a binary search.

That's why everyone here is suggesting the usage of Maps which are a built-in feature of Purebasic, or SQLite which has different strategies to create indices over a column of entries that might improve performance too.
The english grammar is freeware, you can use it freely - But it's not Open Source, i.e. you can not change it or publish it in altered way.
User avatar
skywalk
Addict
Addict
Posts: 4224
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: Checking for Unique Elements in a List()

Post by skywalk »

If you must backup or save your settings or data at some point, it makes more sense to use SQLite.
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
Post Reply