I'm building my first script and I need help to create a loop instead of my repetitiv functions. The aim is to check if every field already exists, and to build it only if it doesn't.

This script does what I want:

layer = iface.activeLayer()
 dpr = layer.dataProvider()
if layer.fields().indexFromName("apr") == -1:
    dpr.addAttributes([QgsField("apr", QVariant.String)])
if layer.fields().indexFromName("iso") == -1:
    dpr.addAttributes([QgsField("iso", QVariant.String)])
if layer.fields().indexFromName("exp") == -1:
    dpr.addAttributes([QgsField("exp", QVariant.String)])
if layer.fields().indexFromName("foca") == -1:
    dpr.addAttributes([QgsField("foca", QVariant.String)])
if layer.fields().indexFromName("foca35") == -1:
    dpr.addAttributes([QgsField("foca35", QVariant.Double)])
if layer.fields().indexFromName("shsp") == -1:
    dpr.addAttributes([QgsField("shsp", QVariant.Double)])
if layer.fields().indexFromName("fyd") == -1:
    dpr.addAttributes([QgsField("fyd", QVariant.Double)])
if layer.fields().indexFromName("gyd") == -1:
    dpr.addAttributes([QgsField("gyd", QVariant.Double)])
if layer.fields().indexFromName("ra") == -1:
    dpr.addAttributes([QgsField("ra", QVariant.Double)])

if layer.fields().indexFromName("pi") == -1:
    dpr.addAttributes([QgsField("pi", QVariant.Double)])

Here's my first attempt to convert it into a loop. This code doesn't work because of synthax, probably this partQVariant.fields_arr[i][1]

Also, I would like to know if there is another way to optimize it? The loop's action performs addAttributes every time. Could not we optimize that by using addAttributes function only one time after the loop?

layer = iface.activeLayer()
 dpr = layer.dataProvider()
fields_arr =  [["apr", "Double"],
                ["iso", "Double"],
                ["exp", "String"],
                ["foca", "Double"],
                ["foca35", "Double"],
                ["shsp", "Double"],
                ["fyd", "Double"],
                ["gyd", "Double"],
                ["ra", "Double"],
                ["pi", "Double"]]
for i in range(0,9):
    if layer.fields().indexFromName(fields_arr[i][0]) == -1:
        dpr.addAttributes([QgsField(fields_arr[i][0], QVariant.fields_arr[i][1])])
  • 1
    I'm not sure of the format in python, but a basic loop goes like this: create a dummyvariable and set it to 0. while [dummyvariable] < [number of times to perform the loop], do [action], [add 1 to dummyvariable]. In your case the action is if layer.fields().indexFromName(fieldname) == -1: dpr.addAttributes([QgsField(fieldname, QVariant.String)]). Create an array of all the fieldname values, and use an array function to get the array item at the array position of your dummy variable. That way each time the loop runs the action, it uses the next fieldname in the list. – csk Dec 17 '19 at 17:02
  • An other difficulty is that there are 2 variants per loop: the field's name and the QVariant that can also changes. – zlikotp Dec 17 '19 at 18:31
  • You could split it into two separate loops, one for QVariant.Double and one for QVariant.String. There might be a more efficient way, but you'll probably get more help if you do a first draft than if you just wait for someone to put all the pieces together for you. – csk Dec 17 '19 at 18:33
  • I've tried something but it doesn't work. (original post edited) – zlikotp Dec 17 '19 at 18:57
  • 1
    Excellent start. I edited your question to add the original script back in, since that one actually worked. I think we're beyond the point where I can help anymore, but this is a good start and I think others will be able to help. – csk Dec 17 '19 at 19:03

If you change the data structure of your fields array slightly, the following should work:

layer = iface.activeLayer()
fields_arr =  [("apr", QVariant.Double),
                ("iso", QVariant.Double),
                ("exp", QVariant.String),
                ("foca", QVariant.Double),
                ("foca35", QVariant.Double),
                ("shsp", QVariant.Double),
                ("fyd", QVariant.Double),
                ("gyd", QVariant.Double),
                ("ra", QVariant.Double),
                ("pi", QVariant.Double)]

for i in fields_arr:
    if i[0] not in [fld.name() for fld in layer.fields()]:
        layer.dataProvider().addAttributes([QgsField(i[0], i[1])])

As for optimization, yes- you could call addAttributes() at the end and pass all fields together in a list.

You could achieve this by creating a list of fields using a list comprehension like:

flds = [QgsField(i[0], i[1]) for i in fields_arr if i[0] not in [fld.name() for fld in layer.fields()]]

For the number of fields you are adding, I can't imagine this would result in any noticeable difference in performance.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.