Search code examples
pythonsortingifc

TypeError: string indices must be integers when using itemgetter with string argument


I am trying to achieve categorisation of an IFC model using IfcOpenShell.

In the first step I extract the the attributes GlobalId and ObjectType from a list of IFC model elements. Then I would like to sort the information using the ObjectType attribute, to receive the following information from the model:

Basiswand:Bestand 08.0:161894
    {'GlobalId': '3vpWoB_K1EZ8RCaYmNGs6M', 'Element': 'Basiswand:Bestand 08.0:161894'}
    {'GlobalId': '3vpWoB_K1EZ8RCaYmNGsB2', 'Element': 'Basiswand:Bestand 08.0:161894'}
Fenster 1-flg - Variabel
    {'GlobalId': '3vpWoB_K1EZ8RCaYmNGssv', 'Element': 'Fenster 1-flg - Variabel'}
    {'GlobalId': '3vpWoB_K1EZ8RCaYmNGsqI', 'Element': 'Fenster 1-flg - Variabel'}

The elements with same ObjectType and with different GlobalId should be combined in one group, to get a categorisation.

rows =[]   
buildingelement = model.by_type('IfcBuildingElement')
for buildingelement in model.by_type('IfcBuildingElement'):
    rows.append(str(buildingelement.GlobalId) + ': ' + str(buildingelement.ObjectType))

print(rows)


from operator import itemgetter 
from itertools import groupby
    # Sort by the desired field first
rows.sort(key=itemgetter('IfcBuildingElement'))
    # Iterate in groups
for date, items in groupby(rows, key=itemgetter('IfcBuildingElement')): 
    print(date)
    for i in items: 
        print(' ', i)

With above code, I get the error message Exception has occurred: TypeError string indices must be integers.


Solution

  • In the first loop, you collect the elements of rows as strings in the form '3vpWoB...: Basiswand...'. For example:

    ['3vpWoB_K1EZ8RCaYmNGs6M: Basiswand:Bestand 08.0:161894',
     '3vpWoB_K1EZ8RCaYmNGsB2: Basiswand:Bestand 08.0:161894',
     '3vpWoB_K1EZ8RCaYmNGssv: Fenster 1-flg - Variabel',
     '3vpWoB_K1EZ8RCaYmNGsqI: Fenster 1-flg - Variabel'
    ]
    

    Then, when you sort and group with itemgetter as key function, you would have to specify a position or range in the string. E.g. when you want to compare based on the 24th character use itemgetter(24) or likewise to compare based on the trailing substring use itemgetter(slice(24,-1)).

    >>> '3vpWoB_K1EZ8RCaYmNGs6M: Basiswand:Bestand 08.0:161894'[24]
    'B'
    >>> '3vpWoB_K1EZ8RCaYmNGs6M: Basiswand:Bestand 08.0:161894'[24:-1]
    'Basiswand:Bestand 08.0:161894'
    

    If you try to use a string as index to get a substring, like in itemgetter('IfcBuildingElement') you get the error you see.

    >>> '3vpWoB_K1EZ8RCaYmNGs6M: Basiswand:Bestand 08.0:161894'['IfcBuildingElement']
    TypeError: string indices must be integers 
    

    So in order to successfully use itemgetter('Element') as key for sorting and grouping, you want to collect the rows as dictionaries of the form {'GlobalId': '3vpWoB...', 'Element': 'Basiswand...'} instead of strings. For example:

    [{'GlobalId':'3vpWoB_K1EZ8RCaYmNGs6M', 'Element':'Basiswand:Bestand 08.0:161894'},
     {'GlobalId':'3vpWoB_K1EZ8RCaYmNGsB2', 'Element':'Basiswand:Bestand 08.0:161894'},
     {'GlobalId':'3vpWoB_K1EZ8RCaYmNGssv', 'Element':'Fenster 1-flg - Variabel'},
     {'GlobalId':'3vpWoB_K1EZ8RCaYmNGsqI', 'Element':'Fenster 1-flg - Variabel'}
    ]
    

    This could be achieved with the following code in the loop to collect the rows.

    rows.append({'GlobalId': buildingelement.GlobalId, 'Element': buildingelement.ObjectType})