Change list structure to match another list structure

Hi, is there a node to put the list on the far right back into the structure of the list on the far left? Element.CurtainGrideline dosnt work retaining the list structure when passed through so needs to be flattened first.

image

Not one node, but two: List.Count and List.Chop.

2 Likes

Sorry I may have worded what I’m trying to do wrong or I cant get the lacing correct. Or maybe I need at levels. Need the list on the right to be matching at sub levels with the list on the left. The list on the left will always be varying and changing

I need to group the list by the other list

The count you want in this instance is list length, so you are looking to get a bunch of 0s then a 2 and a 1 then 0’s… etc… it should set to level 2 I think… then you can feed it into chop…

Hope that helps,

Mark

Hi,

I found a solution to your issue but it’s a little far-fetched.
There is probably a better solution in python.
I don’t understand why the first value in List.Chop is an empty list so I had to cheat by adding an imaginary 0 index.

Edit : Added a shorter graph with List.Chop+

3 Likes

Your List.SublistLengths from the other post makes sense Thank you!. However @L2 gives me 1s instead of 0s and at @L1 it gives dynamo an error. With the 1s im not sure how to flatten the list at the end while holding where it has 2 values in a sublist.

If I could get rid of that array error to I think it would feed into the Element.CurtainGridLine all good and retain its list structure.

This looks amazing! and would have taken me months to work out haha. However, It appears having a count @L1 (I think its this) errors out my dynamo 1.3 so I cant try it :frowning: Opening the script in Dynamo 2.0 just shuts down revit and dynamo instantly.

Specified cast is not valid.

   at ProtoCore.DSASM.Heap.ToHeapObject[T](StackValue heapObject)
   at ProtoCore.Lang.Replication.AtLevelHandler.GetElementsAtLevel(StackValue argument, Int32 level, List`1 indices, Boolean recordIndices, RuntimeCore runtimeCore)
   at ProtoCore.Lang.Replication.AtLevelHandler.<>c__DisplayClass0_0.<GetElementsAtLevel>b__1(StackValue v, Int32 i)
   at System.Linq.Enumerable.<ZipIterator>d__61`3.MoveNext()
   at System.Linq.Enumerable.<SelectManyIterator>d__17`2.MoveNext()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at ProtoCore.Lang.Replication.AtLevelHandler.GetArgumentAtLevel(StackValue argument, AtLevel atLevel, RuntimeCore runtimeCore)
   at ProtoCore.Lang.Replication.AtLevelHandler.GetArgumentsAtLevels(List`1 arguments, List`1 atLevels, RuntimeCore runtimeCore)
   at ProtoCore.Lang.Replication.AtLevelHandler.GetArgumentAtLevelStructure(List`1 arguments, List`1 atLevels, RuntimeCore runtimeCore)
   at ProtoCore.DSASM.Executive.Callr(Int32 blockDeclId, Int32 functionIndex, Int32 classIndex, Boolean& explicitCall, Boolean isDynamicCall, Boolean hasDebugInfo)
   at ProtoCore.DSASM.Executive.CALLR_Handler(Instruction instruction)
   at ProtoCore.DSASM.Executive.Execute(Int32 exeblock, Int32 entry, Language language)
   at ProtoCore.DSASM.Executive.Execute(Int32 exeblock, Int32 entry, List`1 breakpoints, Language language)
   at ProtoCore.DSASM.Executive.BounceUsingExecutive(Executive executive, Int32 exeblock, Int32 entry, StackFrame stackFrame, Int32 locals, Boolean fepRun, Executive exec, List`1 breakpoints)
   at ProtoScript.Runners.ProtoScriptRunner.ExecuteLive(Core core, RuntimeCore runtimeCore)
   at ProtoScript.Runners.LiveRunner.Execute(Boolean isCodeCompiled)
   at ProtoScript.Runners.LiveRunner.CompileAndExecute(List`1 astList)
   at ProtoScript.Runners.LiveRunner.CompileAndExecuteForDeltaExecution(List`1 astList)
   at ProtoScript.Runners.LiveRunner.SynchronizeInternal(GraphSyncData syncData)
   at ProtoScript.Runners.LiveRunner.UpdateGraph(GraphSyncData syncData)
   at Dynamo.Scheduler.UpdateGraphAsyncTask.HandleTaskExecutionCore()
   at Dynamo.Scheduler.AsyncTask.Execute()

The count @L1 works in other scripts. I wonder if this python code replacing the emptys with a value to keep the list structure is doing somthing funky.

list = IN[0]
replace = IN[1]
out = []

for l in list:
	if l == []:
		out.append(replace)
	else:
		out.append(l)

OUT = out

Might be late to the party but thought it would be a fun test of recursive functions in python:

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

def recCount(_lists,c):
	for _list in _lists:
		if isinstance(_list,list):
			c = recCount(_list,c)
		else:
			c += 1
	return c

def recMatch(_list,_matches,_out,c):
	for _match in _matches:
		if isinstance(_match,list):
			outTemp, c = recMatch(_list,_match,[],c)
			_out.append(outTemp)
		else:
			_out.append(_list[c])
			c += 1
	return _out, c
		

# The inputs to this node will be stored as a list in the IN variables.
mList = IN[0]
toMatch = IN[1]
outList = []
c = 0
mC = 0
if len(mList) == recCount(toMatch,c):
	OUT = recMatch(mList,toMatch,outList,mC)[0]
else:
	OUT = ["List lengths do not match.","IN[0] length = "+ str(len(mList)), "IN[1] length = " + str(recCount(toMatch,c))]

Things to note:
mList (IN[0]), which is the list you want to change format of, must be completely flatten. The script will test for flatten and output the error message. It will also check for equal lengths of both lists, regardless of the levels.

I have not tested it out for empty lists though. That is my next step.

9 Likes

Amazing! Thank you. Exactly what I needed. I have got to start learning python!

1 Like

Hi Alban,

I tried replicating your node solution and I’m not sure it works if there are values past your final sub list, the nulls get removed? I wonder if this should be raised as a ‘bug’ with the count node? If not a bug, then a result the user wouldn’t expect…

Kenny, this is massively helpful, have you thought of adding it to a package? Or Sol’s github?

Cheers,

Mark

I mean I just made it to help with this case in a couple minutes, haven’t done any rigorous testing nor do I have a package of my own. If anyone wants to use it or add it to a package, they are free to do so. Adding a credit would be appreciated.

1 Like

Yeah for someone that good at Python it’s a couple of minutes, for the rest of us it’s about 20 Nodes :smiley:

This is the Github I was thinking of… I would say that a lot of people would appreciate your work.

2 Likes

I have never really used GitHub before, how do I contribute? I cleaned up the python and checked for PEP8 standards and made a GitHub account.

Maybe message @solamour? But I think you find the most relevant folder for example concept…

… ‘create new file’ and copy your code in. I’m not sure if you need to be signed in… I have a free account… The fact that this repository and Orchid are on Github was enough for me to sign up! :slight_smile:

1 Like

Seems to be a problem with my computer, my GitHub keeps logging out on its own immediately and when I try to relog in, an error page pops up. I will try when I get home. For now, this is the cleaned up version:

"""
LIST : MATCH LEVELS
-
a dynamoPython script, visit the website for more details
https://github.com/Amoursol/dynamoPython
"""
__author__ = 'Kenzo Bird - birdkenzo@gmail.com'
__dynamoforum__ = 'Kennyb6'
__version__ = '1.0'

"""
Script to match structure of one list to another.
IN[0] must be completely flattened prior to this script.
Empty lists in either inputs may give unwanted results.
"""


def recCount(_lists, c):
    # Count all elements in the structure list (IN[1])
    for _list in _lists:
        if isinstance(_list, list):
            c = recCount(_list, c)
        else:
            c += 1
    return c


def recMatch(_list, _matches, _out, c):
    """
    Recreate the value list (IN[0]) according to the structure list (IN[1])
    _list = entire value list
    _matches = the current list level of the structure list
    _out = value list matching the structure list to be passed on
    c = count to be used as the current index of the value list
    """
    for _match in _matches:
        if isinstance(_match, list):  # if list, recurse another level deeper
            outTemp, c = recMatch(_list, _match, [], c)
            _out.append(outTemp)
        else:
            _out.append(_list[c])
            c += 1
    return _out, c


# Inputs:
mList = IN[0]  # List of values, flattened to single level
toMatch = IN[1]  # Original structured list
outList = []  # Need blank list to be appended, will be new value list
c = 0
mC = 0

# Checks that both lists are equal value to prevent invalid index errors
if len(mList) == recCount(toMatch, c):
    OUT = recMatch(mList, toMatch, outList, mC)[0]
else:
    in0Len = "IN[0] length = " + str(recCount(mList, c))
    in1Len = "IN[1] length = " + str(recCount(toMatch, c))
    OUT = ["List lengths do not match.", in0Len, in1Len]
2 Likes

Hi Mark,

You’re right. If the end of the list is a null, List.Count removes it.
It’s weird.

I’ve just started researching it but you wouldn’t happen to know about the algorithm to fit rectangles in a rectangle… I.e views on sheets :slight_smile: I’m actually looking forward to cracking it. Gonna take a few months of learning coding though

Sorry, never heard of it before.

There was a reference here… Sheet.PackViews from Rhythm?

Hope that helps,

Mark

Oddly enough bin packing (the algorithm to fit rectangles in a rectangle) has been a frequent conversation for me of late… fwiw, single outcome versions are entirely doable with design script, which will help scale your work (the python solutions I have seen require adding libraries which may not exist in other locations).

1 Like