Notifications
Clear all

[Closed] Challenge: Make all Object Names Unique

please give us an example – list of 10 names before renaming and after

I think this is a way to do it… although this would make anything capitalized become lowercase.


--Setup
for i = 1 to 100 do
(
	box name:("TestBox_" + (random 1 20) as string)
)
for i = 1 to 100 do
(
	box name:("testbox_" + (random 1 20) as string)
)

--This function looks through an array of objects and finds the naming clashes and renames them to unique names, can easily be modified for selection etc....
fn makeObjectNamesUnique aObjs =
(
	theObjs = sort(for o in aObjs collect (toLower o.name))
	matches = #()
	for i = 1 to (theObjs.count - 1) where theObjs[i] == theObjs[i + 1] do appendIfUnique matches theObjs[i]

	for m in matches do
	(
		for o in aObjs where (toLower o.name) == m do o.name = uniqueName m
	)
)
makeObjectNamesUnique objects
--makeObjectNamesUnique selection



1 Reply
(@aaandres)
Joined: 11 months ago

Posts: 0

This method fails, Dave.
Try a scene with your “TestBox_”, add 7 Spheres, and rename Sphere002 to Sphere001.
Run your code and you’ll get 2 “sphere005” and 2 “sphere006”.

i don’t think your solution is good enough. it would be good to have highest index is equal to number of dups.

uniqueName make index 004 for example in case where 001 and 003 already exist. i would do something like i’ve showed above.

1 Reply
(@davewortley)
Joined: 11 months ago

Posts: 0

I’m not too bothered by that, I just want to make sure when using later scripts which get objects by name that I don’t have any clashes.

well…

for obj in objects do 
(
	dups = getnodebyname obj.name all:on 
	if dups.count > 0 do for dup in dups do dup.name = uniquename (dup.name as name)
)


1 Reply
(@davewortley)
Joined: 11 months ago

Posts: 0

I did not know about the optional ‘all’ parameter of getNodeByName… interesting…

I really don’t know what happens… where DenisT use 2 lines, I use over 100!!

Well, this is my proposal. It’s based in finding the “index” of the objects with duplicated names through C#.
After that, it use this index array to rename these objects and, additionnally, return original names (with upper and lower case) to objects.
Not quick at all, not very nice structure, but it works.

P.S.: You should create an example with additionnal names, not only “TestBox_”, because I’ve found some problems with the other codes (renaming what you don’t have to).


		global uniqueNamesSearch = "uniqueNamesSearch"
		
		(
			classStr = "
			using System;
			using System.Collections;
			using System.Collections.Generic;
			using System.Linq;
			using Autodesk.Max;


			public class uniqueNamesSearch
			{
				static public IGlobal global = GlobalInterface.Instance;
				public string[] objNames;
				public int[] index;
				public int numObjs;
				public int[] repeatedIndex;

				/// CONSTRUCTOR
				public uniqueNamesSearch(string[] MXSobjnames)
				{
					numObjs = MXSobjnames.Length;
					objNames = new string[numObjs];
					index = new int[numObjs];
					for (int k = 0; k < numObjs; k++)
					{
						objNames[k] = MXSobjnames[k].ToLower();
						index[k] = k + 1;
					}

					Array.Sort(objNames, index);
		 		        repeatedIndex = findRepeated();
				}
				
				


				public int[] findRepeated()
				{
					List<int> repeated = new List<int>();

					for (int k = 1; k < numObjs; k++)
					{
						if (objNames[k - 1] == objNames[k])
						{
							repeated.Add(index[k-1]);
							repeated.Add(index[k]);
						}
					}

					return repeated.Distinct().ToArray();
				}
			}
			"
			compilerParams = dotnetobject "System.CodeDom.Compiler.CompilerParameters"
			dotnet.setlifetimecontrol compilerParams #dotnet

			compilerParams.ReferencedAssemblies.Add("System.dll");
			compilerParams.ReferencedAssemblies.Add("System.Core.dll");
			compilerParams.ReferencedAssemblies.Add((getdir #maxroot) + @"Autodesk.Max.dll");
			compilerParams.GenerateInMemory = on
			csharpProvider = dotnetobject "Microsoft.CSharp.CSharpCodeProvider"

			compilerResults = csharpProvider.CompileAssemblyFromSource compilerParams #(classStr)
			dotnet.setlifetimecontrol compilerResults #dotnet

			if (compilerResults.Errors.Count > 0 ) then
			(
				local errs = stringstream ""
				for i = 0 to (compilerResults.Errors.Count-1) do
				(
					local err = compilerResults.Errors.Item[i]
					format "Error:% Line:% Column:% %
" err.ErrorNumber err.Line err.Column err.ErrorText to:errs
				)
				format "%
" errs
				undefined
			)			
			
		)

		
		(	
			tss = timestamp()
			
			objNames = for o in objects collect o.name
			findRepeatedObjects = dotnetobject uniqueNamesSearch objNames	
			repeatedIndex = findRepeatedObjects.repeatedIndex
			lowerNames = findRepeatedObjects.objNames
			
			for i = 1 to objects.count do objects[i].name = toLower objects[i].name
			for i in repeatedIndex do objects[i].name = uniqueName objects[i].name
			for i = 1 to objects.count where findItem repeatedIndex i == 0 do objects[i].name = objNames[i]
			
			for i in repeatedIndex do
			(
				realName = objNames[i]
				uniqueLowerName = objects[i].name
				baseName = trimright uniqueLowerName "0123456789"
				uniqueIndex = (substring uniqueLowerName (baseName.count+1)  -1)
				finalUniqueName = (trimright realName "0123456789") + uniqueIndex
				objects[i].name =  finalUniqueName
			)
			
			tef = timestamp()
			print ("Total time: " + ((tef-tss)/1000.0) as string + " seconds")
		)
		

My code cleaned:



		global uniqueNamesSearch = "uniqueNamesSearch"
		
		(
			classStr = "
			using System;
			using System.Collections;
			using System.Collections.Generic;
			using System.Linq;

			public class uniqueNamesSearch
			{
				public int[] index;
				public int numObjs;
				public int[] repeatedIndex;

				/// CONSTRUCTOR
				public uniqueNamesSearch(string[] MXSobjnames)
				{
					numObjs = MXSobjnames.Length;
					string[] objNames = new string[numObjs];
					index = new int[numObjs];
					for (int k = 0; k < numObjs; k++)
					{
						objNames[k] = MXSobjnames[k].ToLower();
						index[k] = k + 1;
					}
					Array.Sort(objNames, index);
					repeatedIndex = findRepeated(objNames);
				}

				public int[] findRepeated(string[] objNames)
				{
					List<int> repeated = new List<int>();
					for (int k = 1; k < numObjs; k++)
					{
						if (objNames[k - 1] == objNames[k])
						{
							repeated.Add(index[k-1]);
							repeated.Add(index[k]);
						}
					}
					return repeated.Distinct().ToArray();
				}
			}
			"
			compilerParams = dotnetobject "System.CodeDom.Compiler.CompilerParameters"
			dotnet.setlifetimecontrol compilerParams #dotnet

			compilerParams.ReferencedAssemblies.Add("System.dll");
			compilerParams.ReferencedAssemblies.Add("System.Core.dll");
			compilerParams.GenerateInMemory = on
			csharpProvider = dotnetobject "Microsoft.CSharp.CSharpCodeProvider"

			compilerResults = csharpProvider.CompileAssemblyFromSource compilerParams #(classStr)
			dotnet.setlifetimecontrol compilerResults #dotnet
		)

		
		(	
			tss = timestamp()
			
			objNames = for o in objects collect o.name
			findRepeatedObjects = dotnetobject uniqueNamesSearch objNames	
			repeatedIndex = findRepeatedObjects.repeatedIndex
			
			for o in objects do o.name = toLower o.name
			for i in repeatedIndex do objects[i].name = uniqueName objects[i].name
			for i = 1 to objects.count where findItem repeatedIndex i == 0 do objects[i].name = objNames[i]
			
			for i in repeatedIndex do
			(
				uniqueLowerName = objects[i].name
				baseName = trimright uniqueLowerName "0123456789"
				uniqueIndex = (substring uniqueLowerName (baseName.count+1)  -1)
				objects[i].name =  (trimright objNames[i] "0123456789") + uniqueIndex
			)
			
			tef = timestamp()
			print ("Total time: " + ((tef-tss)/1000.0) as string + " seconds")
		)
		

it should be there

if dups.count > 1 ...
Page 2 / 2