GroupByKey in Python

Hi all,

How can I group my list by keys in python like the Dynamo List.GroupByKey node?

import clr #.NET Laden
import sys #sys is de fundamentele Python bibliotheek
#de standaard IronPython-bibliotheken
#sys.path.append('C:\Program Files (x86)\IronPython 2.7\Lib') #Imports the
#standaard IronPython-bibliotheken, die alles dekken, van servers en
#encryptie tot reguliere expressies.
import System #The System namespace in de hoofdmap .NET
from System import Array #.NET class voor het verwerken van array-informatie
import System.Collections.Generic as MGen #Module kan nu benaderd worden met MGen.xxxx
#from System.Collections.Generic import * #Hiermee kunt u generieke afhandelen. Revit's API
#soms wil hard-getypte 'generieke' lijsten, genaamd ILists. Als je niet nodig hebt
#deze kunt u deze regel verwijderen.
clr.AddReference('ProtoGeometry')  #Een Dynamo-bibliotheek voor zijn proxygeometrie
#classes. Je hebt dit alleen nodig als je interactie hebt met geometrie.
import Autodesk.DesignScript.Geometry as AGeo #Module kan worden opgeroepen a=met AGeo.xxxx
#from Autodesk.DesignScript.Geometry import * #Laadt alles in Dynamo's
#geometriebibliotheek
clr.AddReference("RevitNodes") #Dynamo's nodes voor Revit
import Revit #Laad in de Revit-namespaces in RevitNodes
clr.ImportExtensions(Revit.Elements) #Meer laden van Dynamo's Revit-bibliotheken
clr.ImportExtensions(Revit.GeometryConversion) #Meer laden van Dynamo's
#Revit-bibliotheken. Je hebt dit alleen nodig als je interactie hebt met geometrie.
clr.AddReference("RevitServices") #Dynamo's classes voor het omgaan met Revit-documenten
import RevitServices 
from RevitServices.Persistence import DocumentManager #Een interne Dynamo class
#dat het document bijhoudt waaraan Dynamo momenteel is gekoppeld
from RevitServices.Transactions import TransactionManager #Een Dynamo class voor
#transacties openen en sluiten om de database van het Revit-document te wijzigen

clr.AddReference("RevitAPI") #Verwijzing naar Revit's API DLL's toevoegen
clr.AddReference("RevitAPIUI") #Verwijzing naar Revit's APIUI DLL's toevoegen

import Autodesk #Loads the Autodesk namespace
import Autodesk.Revit.DB as RDB #Loading Revit's API UI classes module kan nu worden aangeroepen met RDB.xxxx
#from Autodesk.Revit.DB import * #Loading Revit's API UI classes
import Autodesk.Revit.UI as RUI # Loading Revit's API UI classes als RUI.xxxx
#from Autodesk.Revit.UI import * #Loading Revit's API UI classes

doc = DocumentManager.Instance.CurrentDBDocument #Dit is het actieve Revit document
uiapp = DocumentManager.Instance.CurrentUIApplication #Een handle instellen voor het actieve Revit UI-document
app = uiapp.Application  #Een handle instellen op de momenteel geopende instantie van de Revit-toepassing
uidoc = uiapp.ActiveUIDocument #Een handle instellen op de momenteel geopende instantie van de Revit UI-toepassing
revit_version = int(doc.Application.VersionNumber)
# code omrekenen revit feet naar huidig ingestele document units


	
#einde code omrekenen revit feet naar huidig ingestele document units

#alle elementen van OST category ophalen
alleElementen = RDB.FilteredElementCollector(doc)
alleElementen.OfCategory(RDB.BuiltInCategory.OST_PipeCurves)
alleElementen.WhereElementIsNotElementType()

MyElem = alleElementen.ToElements()
SystemName=[] #lege lijst starten

for NE in MyElem:
	Section=NE.get_Parameter(RDB.BuiltInParameter.RBS_SYSTEM_NAME_PARAM).AsString()
	SystemName.append(Section)

gezipt = zip(MyElem, SystemName)

#wegschrijven naar uitvoer

OUT= gezipt


I want to group my pipes by the output of SystemName

1 Like

This is my goto solution for python grouping, alternatively if you have itertools available its groupby() method works well if you turn your list into a dictionary.

2 Likes

I’m not entirely sure what’s going on in your specific python code, but this is an effective code to group things by keys:

def unique(items):
	ulist=[]
	for i in items:
		if i not in ulist:
			ulist.append(i)
	return ulist

items = IN[0]
keys = IN[1]

ukeys = unique(keys)

#create empty output list
grplist = []
for i in range(len(ukeys)):
	grplist.append([])

#find indices
inds = []
for key in keys:
	inds.append(ukeys.index(key))

#fill output list
for item, ind in zip(items,inds):
	grplist[ind].append(item)


OUT = grplist

I would absolutely not be surprised if there’s more elegant methods built into Python but this certainly works.

1 Like

Hi,
an alternative using PipingNetwork property

import clr
import sys
import System
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
import Autodesk.DesignScript.Geometry as DS

#import Revit API
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB
#import specify namespace
from Autodesk.Revit.DB.Plumbing import *


clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

doc = DocumentManager.Instance.CurrentDBDocument

pipeSystems = DB.FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_PipingSystem).WhereElementIsNotElementType()

OUT = {p_sys.Name : [x for x in p_sys.PipingNetwork if isinstance(x, Pipe)] for p_sys in pipeSystems}

or with itertools.groupby mentioned by @GavinCrump

4 Likes

Little update, you can just define this GroupByKey function.
It gives you an output with both the grouped lists and the unique keys, just like the node.

def GroupByKey(Items,Keys):
	UKeys=[]
	for Key in Keys:
		if Key not in UKeys:
			UKeys.append(Key)
	GrpList=[]
	for empt in range(len(UKeys)):
		GrpList.append([])
	Inds=[]
	for Key in Keys:
		Inds.append(UKeys.index(Key))
	for Item, Ind in zip(Items, Inds):
		GrpList[Ind].append(Item)
	return [GrpList,UKeys]
4 Likes

Thank you all guys for the solutions, unfortunately I can only give one solution so choose the one I used.

1 Like

@PauLtus
How can I group inside a group?
Like levels in Dynamo?

	
def GroupByKey(Items,Keys):
	UKeys=[]
	for Key in Keys:
		if Key not in UKeys:
			UKeys.append(Key)
	GrpList=[]
	for empt in range(len(UKeys)):
		GrpList.append([])
	Inds=[]
	for Key in Keys:
		Inds.append(UKeys.index(Key))
	for Item, Ind in zip(Items, Inds):
		GrpList[Ind].append(Item)
	return [GrpList,UKeys]

	
#einde code omrekenen revit feet naar huidig ingestele document units

cat_list = [RDB.BuiltInCategory.OST_PipeCurves, RDB.BuiltInCategory.OST_PipeFitting]
typed_list = MGen.List[RDB.BuiltInCategory](cat_list)
filter = RDB.ElementMulticategoryFilter(typed_list)
output = RDB.FilteredElementCollector(doc).WherePasses(filter).WhereElementIsNotElementType().ToElements()


#systemname ophalen
SystemName=[] #lege lijst starten
for NE in output:
	SystemN=NE.get_Parameter(RDB.BuiltInParameter.RBS_SYSTEM_NAME_PARAM).AsString()
	SystemName.append(SystemN)

#Group list
grplist = GroupByKey(output, SystemName)

#Grouped list 0 = elements 1 = keys
groups = grplist[0]
uniquekeys = grplist [1]

#check parameter in de groep

Section = []
for NE in groups:
	try:
		SectionN= NE.get_Parameter(RDB.BuiltInParameter.RBS_SECTION).AsValueString()
		Section.append(SectionN)
	except:
		Section.append(None)

grplist_1 = GroupByKey(groups, Section)

OUT = Section

image

Something like that doesn’t exist in Python, you have to play around with for loops then.
When I started with Python I quite missed these levels, but doing it in Python is certainly more consistent.

1 Like

Thanks, I had a python course today and the teacher told the same, use a for loop a loop for example

It’s possible to use levels if you put this inside a custom node. You can then work across a data structure in a similar manner to a normal node, and apply lacing behaviour as well.

3 Likes

Thanks Gavin, yes ofcourse that is a way. :slight_smile:

1 Like

Can someone help me with this?

I want to group inside a grouped list, this is the grouped list:
image

The “section” return as a null:

image


import Autodesk #Loads the Autodesk namespace
import Autodesk.Revit.DB as RDB
	
def GroupByKey(Items,Keys):
	UKeys=[]
	for Key in Keys:
		if Key not in UKeys:
			UKeys.append(Key)
	GrpList=[]
	for empt in range(len(UKeys)):
		GrpList.append([])
	Inds=[]
	for Key in Keys:
		Inds.append(UKeys.index(Key))
	for Item, Ind in zip(Items, Inds):
		GrpList[Ind].append(Item)
	return [GrpList]

def GetName(ele):
	elename = None
 	try:
 		elename = ele.Name
 	except:
 		elename = RDB.Element.Name.__get__(ele)
 	return elename 

	
#einde code omrekenen revit feet naar huidig ingestele document units

#categoriëen ophalen
cat_list = [RDB.BuiltInCategory.OST_PipeCurves, RDB.BuiltInCategory.OST_PipeFitting]
typed_list = MGen.List[RDB.BuiltInCategory](cat_list)
filter = RDB.ElementMulticategoryFilter(typed_list)
elementen = RDB.FilteredElementCollector(doc).WherePasses(filter).WhereElementIsNotElementType().ToElements()

#pipes en fittings ophalen 
pipe_elem = []
for nw in elementen:
	p_name = GetName(nw)
	if p_name.Contains("Bochtstraal") or p_name.Contains("Alupex"):
		pipe_elem.append(nw)

#elementen groeperen op systemname
systems = []
grplist = []

for np in pipe_elem:
	system_name = np.get_Parameter(RDB.BuiltInParameter.RBS_SYSTEM_NAME_PARAM).AsString()
	systems.append(system_name)

grplist = GroupByKey(pipe_elem, systems)

#buizen uit de groep halen
section = []
grp_elem_system = grplist [0]
for np in grp_elem_system:
	try:
		section_name = np.get_Parameter(RDB.BuiltInParameter.RBS_SECTION).AsString()
		section.append(system_name)
	except:
		section.append(None)

OUT = section

BTW, I want only python :wink:

Anyone? :slightly_smiling_face:

You’ll need to take the logic one level deeper using a for loop I think. That or use a custom node with levels (if you want versatility that is my recommended approach).

Thanks for you’re reply

Do you have an example how to do this?

I know, but I try to build it without any custom nodes or input.
It is the hardest way but the best way to learn I guess

def GroupByKey(Items,Keys):
	UKeys=[]
	for Key in Keys:
		if Key not in UKeys:
			UKeys.append(Key)
	GrpList=[]
	for empt in range(len(UKeys)):
		GrpList.append([])
	Inds=[]
	for Key in Keys:
		Inds.append(UKeys.index(Key))
	for Item, Ind in zip(Items, Inds):
		GrpList[Ind].append(Item)
	return [GrpList,UKeys]
 
dataToGroup  = IN[0]
keysForGroup = IN[1]

groupedData = []

for d,k in zip(dataToGroup,keysForGroup):
	grouped = GroupByKey(d,k)
	groupedData.append(grouped[0])
	
OUT = groupedData

Thanks Gavin, almost the solution:

I need the keys 1 level deeper (@L2 and @L3)

import Autodesk.Revit.DB as RDB
import System.Collections.Generic as MGen

def GroupByKey(Items,Keys):
	UKeys=[]
	for Key in Keys:
		if Key not in UKeys:
			UKeys.append(Key)
	GrpList=[]
	for empt in range(len(UKeys)):
		GrpList.append([])
	Inds=[]
	for Key in Keys:
		Inds.append(UKeys.index(Key))
	for Item, Ind in zip(Items, Inds):
		GrpList[Ind].append(Item)
	return [GrpList, UKeys]

def GetName(ele):
	elename = None
 	try:
 		elename = ele.Name
 	except:
 		elename = RDB.Element.Name.__get__(ele)
 	return elename 

	
#einde code omrekenen revit feet naar huidig ingestele document units

#categoriëen ophalen
cat_list = [RDB.BuiltInCategory.OST_PipeCurves, RDB.BuiltInCategory.OST_PipeFitting]
typed_list = MGen.List[RDB.BuiltInCategory](cat_list)
filter = RDB.ElementMulticategoryFilter(typed_list)
elementen = RDB.FilteredElementCollector(doc).WherePasses(filter).WhereElementIsNotElementType().ToElements()

#pipes en fittings ophalen 
pipe_elem = []
for nw in elementen:
	p_name = GetName(nw)
	if p_name.Contains("Bochtstraal") or p_name.Contains("Alupex"):
		pipe_elem.append(nw)

#elementen groeperen op systemname
systems = []

for np in pipe_elem:
	system_name = np.get_Parameter(RDB.BuiltInParameter.RBS_SYSTEM_NAME_PARAM).AsString()
	systems.append(system_name)

grplist = GroupByKey(pipe_elem, systems)
#section ophalen en groeperen


dataToGroup  = grplist[0]
keysForGroup = []
for np in pipe_elem:
	try: 
		section_name = np.get_Parameter(RDB.BuiltInParameter.RBS_SECTION).AsValueString()
		keysForGroup.append(section_name)
	except:
		keysForGroup.append("0")

gezipt = zip(dataToGroup, keysForGroup)
groupedData = []

for d,k in gezipt:
	grouped = GroupByKey(d,k)
	groupedData.append(grouped[0])
	
OUT = keysForGroup, dataToGroup