Invert list grouping


#1

Greenings all,

I need to find all the elements that are the boundaries of the rooms and write down the rooms names into these elements. If one element is the boundary of several rooms, then write down all the rooms names separated by commas.
I can not solve the intermediate task with lists:

How to get such list:
Element1
|____Room1
|____Room2
Element2
|____RoomN

Please help!


#2

List.GroupByKey and use the “Elements” as the keys.


#3

@Sean1 I am not sure that it would be this easy. Please provide an image of your proposed solution so that we can try it out.

Now my understanding is that @NurlanAdsk wants to create a list of all bounding elements (walls?), and then under each wall list what rooms are bound by this wall.

Here’s my take on it:

Code for the custom Python node:

# Copyright(c) 2018, Konrad K Sobon
# @arch_laboratory, http://archi-lab.net

import clr
clr.AddReference("RevitNodes")

import Revit
clr.ImportExtensions(Revit.Elements)
clr.AddReference("RevitAPI")

from Autodesk.Revit.DB import *

try:
    errorReport = None
    output = [[] for i in range(len(IN[0]))]
    for index1, i in enumerate(IN[0]):
    	for index2, j in enumerate(IN[1]):
    		if i in j:
    			output[index1].append(IN[2][index2])

except:
    # if error occurs anywhere in the process catch it
    import traceback
    errorReport = traceback.format_exc()

# Assign your output to the OUT variable
if None == errorReport:
    OUT = output
else:
    OUT = errorReport

Basically the idea is that we first get all of the rooms, and their bounding elements. I flatten the list of bounding elements because one wall can bound multiple rooms, so we want to remove all duplicates. Next, I use a little Python code, to iterate over every unique wall, that we know participates in bounding of at least one room, and add them to a list. The output here basically lists what rooms are bound by each wall.

Cheers!


#4

Hello Konrad,

Thank you for your solution and for the archi+lab package! Great job! :+1:

As a result, I need to fill in the rooms information in all the elements that are boundaries, as well as in some categories located in the rooms (crossing them). I will try to use your code for the whole task, I hope I can figure it out

Best wishes,
Nurlan


#6

Hi Konrad,

Really interesting code, the ways that you have used enumerates and defined output are new to me. I’ve had a go at trying to understand it, I would be very appreciative if you could comment my interpretation…

# Copyright(c) 2018, Konrad K Sobon
# @arch_laboratory, http://archi-lab.net
# markup by MKA who takes no credit and all the blame

import clr
clr.AddReference("RevitNodes")

import Revit
clr.ImportExtensions(Revit.Elements)
clr.AddReference("RevitAPI")

from Autodesk.Revit.DB import *

import sys
sys.path.append(r'C:\Program Files (x86)\IronPython 2.7\Lib')


# define inputs
wallsHeader = IN[0]
walls = IN[1]
rooms = IN[2]
rng = []
output = []


try:
    errorReport = None
    # create a range of length wallsHeader
    for WH in range(len(wallsHeader)):
        rng = []
        #define the structure of the output has having a list length rng
        output.append(rng)
    for indexWH, WH in enumerate(wallsHeader):
    	for indexRM, RM in enumerate(rooms):
    		if WH in RM:
    		    #for the index of wall header, append the wall at index of room
    			output[indexWH].append(walls[indexRM])

except:
    # i added a library call above
    # if error occurs anywhere in the process catch it
    import traceback
    errorReport = traceback.format_exc()


# Assign your output to the OUT variable
if None == errorReport:
    OUT = output
else:
    OUT = errorReport

Thankyou for your time,

Mark


#7

Random tangent but just for fun:

Both your
for WH in range(len(wallsHeader)):
and his
output = [[] for i in range(len...
can be shortened to just
output = [[] for i in IN[0]]
as you don’t need the actual element inside the for loop, just the iteration amount, which is the same in either process.

See this test.

Also you can directly append [] to a list, without the need for rng = [] inside the for loop.


#8

Hi Kenny,

Thankyou for your input.

My commenting is just that I can’t understand the concise code and have to make it really obvious… So mine is going to be way longer to break down each step into it’s bits…

Yours is even more elegant than Konrads, therefore even harder for me to understand :smiley: at least if there’s a range, it helps me know that we’re talking about the length of something… :stuck_out_tongue:

Now I know that, I can use your method in future…

Thanks again,

Mark


#9

Sorry, I was just trying to show why range(len()) can be a bit misleading in this situation but I didn’t explain it.

All mine and Konrad’s shortened version is basically boiling down for loops into a single line with a slightly different syntax. It ends up reading like an English sentence and can work for simple if statements as well. But the principle is the same as

for WH in range(len(wallsHeader)):
        rng = []
        output.append(rng)

Except the final line is output.append([])

My change is that since we do not actually need WH in the output of the for loop, there is no need to do range(len()). A simple for i in wallsHeader would work just as well because we just want it to iterate. So for every time it gets to an item in the list, append [].

If this is unnecessary, sorry.


#10

Hi Kenny,

I didn’t mean to sound angry, I was joking, sorry if that didn’t come across, thank you for your explanation :smiley:

Mark


#11

@NurlanAdsk Apologies for high-jacking your thread.

Here’s a node version which is about right…

It’s ugly and slow however, I’m sure someone can improve it if you want to go down this route…

Hope that’s of interest,

Mark

RoomDataIntoWall-1.dyn (35.2 KB)


#12

Hi guys!
Thanks for participating and for the solutions provided, I really appreciate it.

Is it possible to get such a list by changing the code a little? Sorry, I’m a complete newbie in Python and Dynamo :pleading_face:. Or I should start a new topic?

List0
|____Wall1 (as element)
|____Room1 (as element)
|____Room2

ListN
|____WallN
|____RoomN

There is a “Tool.GetSurroundingElements” from the SteamNodes that gives a list of the desired format, but unfortunately gives the wrong result, because it uses Bounding Box.


InvertList_d2.dyn (22.1 KB)


#13

Hi Mark,

Thank you for you input. It is very interesting to me. I will now plunge into the logic of lists and python right in front of the deadline :sweat_smile:


#14

Hey,

Try this… Watch out for the @L1 and @L2 in the List.AddItemToFront…

Cheers,

Mark


#15

Dear kennyb6,

Could you show me how the whole code will look like with your simplification, please :pleading_face:


#16

@Konrad_K_Sobon’s code is the simplest version of this. My post was just to simplify a single line within it, which changes nothing of the function.


#17

#18

@NurlanAdsk just a quick comment. When you ask a question, try not to create new questions later in the post. It confuses people. Also, this post had an answer awarded already, and the fact that you asked yet another question with potentially new answer creates a situation where one user will not be rewarded with an approved answer. Let’s not do that. Start a new post next time.