delete an element with a for loop in a geojson file

Viewed 455

for a .geojson lambda file :

{ "type": "FeatureCollection",
    "features": [
      { "type": "Feature",
        "geometry": {
          "type": "Point",
          "coordinates": [102.0, 0.5]
          },
          "properties": {
            "label": "value0",
             ...
          }
        },
       { "type": "Feature",
         "geometry": {..

I would like to delete "Point" and "Polygon" and even "MultiPolygon" type of structures which have a key 'label' in properties.

   import json

   file = open('file.geojson', 'r')
   data = json.load(file)
   
   for k in data['features']:
       if 'label' in k['properties']:
           print('ok') #displays the right number of structure, so the loop works
           del k #doesn't work
           #data['features'].remove(k) works but delete only a part of elements in the file..
           print('okk') #displays so del doesn't work
   
   data_srt = open('file.geojson', 'w')
   data_srt.write(json.dumps(data, ensure_ascii=False, indent=2))
   data_srt.close()

These solutions don't work, why ? Thank you very much.

1 Answers

Consider this example:

l = [1, 2, 3]
for i in l:
    l.remove(i)

print(l) # Prints '[2]'

During the first iteration i == l[0]. l.remove(i) would then be the same as l.remove(l[0]). During the second iteration, i == l[1]. At this point, though, l == [2, 3], because l[0] was removed. So, in the second iteration, l.remove(i) is the same as l.remove(l[1]). After this is executed, l == [2]. If the loop tries to continue to a third iteration, i == l[2]. However, now that l == [2], len(l) == 1 so l[2] is out of bounds. Because of this, the for loop stops now, even though l is not empty. This is essentially the same problem that you are experiencing.

To fix this:

i = len(l) - 1
while i >= 0:
    l.remove(l[i])
    i -= 1

Iterating over a list backwards like this avoids the out-of-bounds problem that was encountered before.

To apply this concept in your situation, this is the solution:

i = len(data["features"]) - 1
while i >= 0:
    if "label" in data["features"][i]["properties"]:
        data["features"].pop(i)
    i -= 1

I just came up with a new, better solution (it uses the reversed() function):

for k in reversed(data["features"]):
    if "label" in k["properties"]:
        data["features"].remove(k)

This uses the same concept of backwards-iterating, but the reversed() function takes care of that for you.

The reason why the del statement had no functionality for you is caused by a more complex concept. I'll will do my best to explain (here is another answer that sort of helps explain it: https://stackoverflow.com/a/14814847/13911868).

When iterating through a list, or any container, in a for loop, like this:

l = [1, 2, 3]
for i in l:
    del i

The i variable is a deep copy of an item in the l, not a reference. That being the case, del i would delete the copied item only, not deleting the original item from l.

On the other hand, in this example:

l = [1, 2, 3]
for i in range(len(l)):
    del l[i]

del l[i] will delete the original item in l because l[i] returns that original object, not a copy.

In this example, though, you will encounter the same out-of-bounds problem as before, so a working solution using the del statement would be:

for k in reversed(range(len(data["features"]))):
    if "label" in data["features"][k]["properties"]:
        del data["features"][k]
Related