Python Data-Matching

I am new to python and would like to know how to go about developing a script to select elements based on data matching. I can already do this in the standard dynamo environment, but I was hoping for a more self-contained method using python.

Inputs
0 = Elements
1 = Parameter Name
2 = Values to match

Out = Matched Elements

What happens in between is magic as far as i am concerned.

At the moment I just need to gauge if this is something which could be accomplished by a complete novice, or am I biting off more than i can chew?

Cheers,
Matt

Hello @m.owens ,

Yes, this is a perfect task to start with Python in Dynamo because it is an easy for-loop and you can achieve it by using dynamo methods.
Here is the code you are looking for:

# Enable Python support and load DesignScript library
import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

# The inputs to this node will be stored as a list in the IN variables.
elems = IN[0]
parName = IN[1]
parValue = IN[2]
# Place your code below this line

MatchElem = []

for e in elems:
	if e.GetParameterValueByName(parName) == parValue:
		MatchElem.append(e)

# Assign your output to the OUT variable.
OUT = MatchElem 

Then, when you’ll be more confident with Python, you will probably like to condense more the code.
In this specific case, everything could be written in one line:

OUT = [e for e in elems if e.GetParameterValueByName(parName) == parValue]
4 Likes

Hi Giuseppe,

Yes, easy for you!

I have a small amount of experience with writing scripts (in VB.NET for Grasshopper) none of which involved looping.

So I expected I would have to import many more Libraries and to unwrap elements. Since you were so willing to help, I’d like to ask a few more questions, and maybe one more favor.

The first issue I am running into is sending two lists into the node. I think I need to use a While Loop to do cross-lacing, right? I don’t know how to do that :(((

Questions:

  1. What does unwrapping an element do, and when do I need to use it?
  2. I assume represents a list you are creating with the name MatchElem. Is that correct?
    In that case, I could also make one for Elements which do not match, right?
  3. If yes, can you please review this code?

MatchElem =
noMatchElem =

for e in elems:
if e.GetParameterValueByName(parName) == parValue:
MatchElem.append(e)
noMatchElem.append(e)

Assign your output to the OUT variable.

OUT = MatchElem

  1. How do I send both of my lists to the output?

Thank you for the help!

Cheers,
Matt

Hello @m.owens,

I can help with a few of your queries!

  1. Wrapping in Dynamo refers to taking something from the Revit API (i.e a Wall) and wrapping it with a Dynamo object so that you can do Dynamo things to it. Easy examples are query it’s name, set parameters, get parameters etc using Nodes. You can see an example here: https://github.com/Amoursol/dynamoPython/wiki/Wrapped-vs.-Unwrapped-Elements

  2. The syntax of [ ] inside of Python demarcates a List, and in Python you have to create an empty list (i.e a basket to catch things in) first and then you cycle through things (iterate through) and if they pass muster, you add them to that bucket (empty list). So, yes you can indeed create one for Elements that do not match :slight_smile:

  3. Code review below.

  4. If you want to assign more than one item to the Output, you simply need to use a comma:
    OUT = item1, item2, item3

Code review:

MatchElem = []
noMatchElem = []

for e in elems:
    if e.GetParameterValueByName(parName) == parValue:
        MatchElem.append(e)
    else:
        noMatchElem.append(e)

This works as follows:

  1. Create two empty lists, one for elements that Match and one for elements that Do not Match.
  2. Iterate through every single element (e) inside our input list (elems)
  3. For every single element inside this input list, check if it has a parameter value that matches your chosen value.
  4. If it does match, then you add it to your MatchElem catchment list.
  5. If it does not match, you use an else statement for those that fail the if statement.
  6. So if it fails the if then you add it to the noMatchElem list.
5 Likes

Hi Solamour,

Thank you for answering my questions, especially wrapping and unwrapping elements.

I found another post which explained how to loop fairly well…it took me some time to figure out how to do it, but I was able to cycle through nested lists. Would LOVE to know how people are posting code…

Here is how it looks at the moment. I changed some List Names to something which made more sense.

elems = IN[0]
parName = IN[1]
parValue = IN[2]

matches =
rejects =

for p in parValue:
for e in elems:
if e.GetParameterValueByName(parName) == p:
matches.append(e)
else:
rejects.append(e)

OUT = matches, rejects

The new issue I have is that each “reject” ist being appended to the list with each cycle. Matches are also ending up in that list. I’ve done a couple of searches but have netted only Dynamo-related posts and it is getting late.

I have seven walls and four values.

I have three matches. I wouldn’t expect there would be four walls in the rejects list, but I sure wish there would be. I’m not sure how to only add the items once when cycling through the lists, or how to operate on the lists (remove duplicates, for example.)

1 Like

This may help you get there - in particular, the sublist bit :slight_smile:

list1 = IN[0]
list2 = IN[1]

results = []

for number in list1: # Taking every single number in List1
    sublist = []
    for letter in list2: # Then taking every singel letter in List2
        sublist.append(str(number) + letter) # Turn the number into a string with str() and concatenate them together
    results.append(sublist)

OUT = results
1 Like

If you still can’t figure it out, I can stop making up mock tests and build something in Revit to solve for real :smiley:

I think you may have been posting while I was editing.

I now have a matched list (perfect) with an unmatched list containing matched and unmatched items (dreadful.) How do I only add the unmatched items once…or…how do I remove duplicates and then subtract list “matches” from list “rejects?”

Of course you can post something that works, but I’m afraid I won’t understand it.

It is quite late here, and I’ll have to try and pick this up tomorrow.

Thanks for all your help!

This is one way of solving it… but I’m a little perplexed as to why it’s not solving your way (Then again… it’s 11:27pm and I’m probably missing obvious things :stuck_out_tongue: )

import clr
clr.AddReference('DSCoreNodes')
from DSCore import *

elementList = IN[0]
parName = IN[1]
parValue = IN[2]

matches = []
rejects = []

for ele in elementList:
    for val in parValue:
        if ele.GetParameterValueByName(parName) == val:
            matches.append(ele)

for ele in elementList:
    if ele not in matches:
        rejects.append(ele)

OUT = matches, rejects
1 Like

an another way

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

elems = IN[0]
parName = IN[1]
parValues = IN[2]

match = []
notmacth = []
for e in elems:
	if any([e.GetParameterValueByName(parName) == x for x in  parValues]) :
		match.append(e)
	else:
		notmacth.append(e)		

OUT = match, notmacth
2 Likes

Brilliant! Exactly what i was looking for:

if ele not in matches:

I “wrote” my first loop !! It was all worth it now :slight_smile:

If I could bother you with just a few more questions:

“ele” is a “variable” correct?

“matches” is a “list”

I guess that “GetParameterValueByName” ist a “method” ?

what is “not in”

Hi c.poupin,

This almost works, but it doesn’t append items to the list in the correct order. There is also an “in” missing at “x for x in parValues”

grafik

Can someone tell me how to post code?

added missing “in”
to post code, past it, select it and click “</>”
2020-05-19 10_43_29-Python Data-Matching - Lists-Logic - Dynamo

1 Like

code updated to sort

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

elems = IN[0]
parName = IN[1]
parValues = IN[2]

match = []
notmacth = []
for e in elems:
	if any([e.GetParameterValueByName(parName) == x for x in parValues]) :
		match.append(e)
	else:
		notmacth.append(e)		
match.sort(key = lambda x : parValues.index(x.GetParameterValueByName(parName))) 
OUT = match, notmacth
1 Like

Thank you for the code-button. I though I had already tried that.

I will try the updated code when I have some time later this evening or later this week!

It seems straight-forward enough and at the first glance, it looks like another very useful sorting function I was looking for!!

Cheers :slight_smile:

As an aside - my solution here isn’t ideal because it uses two loops which makes it less efficient (i.e Get all the things and check all the things then get all the things again and check all the things again).

I would suggest using @c.poupin’s solution :slight_smile: Speed will be negligible here, but good practise is good practise, especially when you scale!

As an aside, you can use TuneUp to profile both approaches and see the speed differences if you are in Revit 2021 too!

1 Like