Notifications
Clear all

[Closed] create arrays of similar file names

I have a few hundred texture maps (from Arrowway) and I would like to rough out some materials with them. Typically there are three textures for each wood species with a suffix for bump, diff and reflection:

wood-036_silver-fir-1_b.png
wood-036_silver-fir-1_d.png
wood-036_silver-fir-1_r.png
wood-036_silver-fir-2_b.png
wood-036_silver-fir-2_d.png
wood-036_silver-fir-2_r.png

I would simply group every three files together, sort the array and build materials based on the index. But the folder has several files that don’t match that pattern. For some, there are alternate diff maps for example.

I would like to collect all files that match pattern up to the suffix, based on the base filename. In the example list above, collect all the files for “wood-036_silver-fir-1” and parse the suffixes to create a material. Then the same for the next base file name.

I can do the parsing of the array of three filenames to create the material, i just need the logic to collect the similar names…

p.s. the forum search seems broken or I’d have searched for this… sorry.

5 Replies

A simple build-in method could be using matchPattern(). Otherwise you could do it with RegEx, which is better and faster for this sort of things. But I think matchPattern() will do just fine.
Below is an example, not fully functional though.

(
  	
  	fn GetFilesFromFolder folder: =
  	(
  		files = #()
  		append files "wood-036_silver-fir-1_b.png"
  		append files "wood-036_silver-fir-1_d.png"
  		append files "wood-036_silver-fir-1_r.png"
  		append files "wood-036_silver-fir-2_b.png"
  		append files "wood-036_silver-fir-2_d.png"
  		append files "wood-036_silver-fir-2_r.png"
  		
  		for j in files do
  		(
  			print (matchPattern j pattern:"wood-036_silver-fir-*" ignoreCase:true)
  		)
  	)
  	
  	GetFilesFromFolder()
  	
  )

find all files in a directory that match a wildcard:

in our case the wildcard is *_?.png

files = getfiles (dir + "/*_?.png")

now we need a way to split filename on texture ‘name’ and texture ‘type’ specified by a latter… let’s do it as:

name = substring file 1 (file.count-1)
type = file[file.count]

(optional) check that a type is one of available

if (finditem #("b","d","r") type) > 0 do

group files by their base ‘name’. i would use a structure:

struct TextureStruct (base, textures = #())

we need a search method to find right group for a file:

fn findByBase list name =
(
    n = 0
    for k = 1 to list.count while n == 0 where list[k].base == name do n = k
    n
)

now we have everything to group:

files = getfiles (dir + "/*_?.png)
types = #("b","d","r") 
textures = #()
for file in files do 
(
    file = getfilenamefile file
    name = substring file 1 (file.count-1)
    type = file[file.count]
    if (finditem types type) > 0 do
    (
         if (k = findbybase textures name) == 0 do 
         (
              append textures (TextureStruct base:name)
              k = textures.count
         )
         append textures[k].textures file
     )
)

that’s it!

(not tested)

it would be better to use lower case for search and strings compare

спасибо Denis!

Thank you very much for the breakdown Denis.
I would be fascinated to see the regex version of this as well.

Jorge, you missed the part where I have hundreds of files to sort (with about 120 base file names).
Some of them have only the _d and _r suffix. Denis’ solution works wonders AND I learned a few things.

1 Reply
(@polytools3d)
Joined: 11 months ago

Posts: 0

I am sorry it wasn’t enough.

Here is a different approach. It returns an array with 3 elements arrays:
1. Texture name
2. Prefix
3. Sufix

#( #(“wood-036_silver-fir-1_b.png”, “wood-036_silver-fir-1”, “b”), …)

  It should work even if you have mixed formats (.png, .jpg, etc.)
  You can also define the separator and if you want the full path of the file.
  
  Hope it works. 
(
      	
      	fn GetFilesByGroup folder separator: fullPath:true =
      	(
      		if folder != undefined do
      		(
      			groups = #()
      			files = getFiles (folder + "//*.*")
      			fileNames = for j in files collect filenameFromPath j
      
      			for j = 1 to fileNames.count where fileNames[j] != () do
      			(
      				filtered = filterString fileNames[j] separator splitEmptyTokens:false
      				prefix = filtered[1]
      				sufix = getFileNameFile filtered[filtered.count]
      				for i = 2 to filtered.count-1 do prefix += separator + filtered[i]
      				
      				if fullPath then grp=#(#(files[j], prefix, sufix)) else grp=#(#(fileNames[j], prefix, sufix))
      
      				for i = j+1 to fileNames.count while ((matchPattern fileNames[i] pattern:(prefix+"*") ignoreCase:true)) do
      				(
      					filtered = filterString fileNames[i] separator splitEmptyTokens:false
      					sufix = getFileNameFile filtered[filtered.count]
      					
      					if fullPath then
      					(
      						append grp #( #(files[i], prefix, sufix) )
      					)else(
      						append grp #(#(fileNames[i], prefix, sufix))
      					)
      					fileNames[i] = ()
      				)
      				append groups grp
      			)
      			return groups
      		)
      	)
      	
      	folder = getSavePath initialDir:""
      	files = GetFilesByGroup folder separator:"_" fullPath:false
      	
      )

here is a regex version with better structuring:

struct TexturesData 
(
	TextureStruct = 
	(
		struct TextureStruct (file, base, type)
	),
	
private 
	pattern = "^(.*?)(_[b,d,r])$",
	regex = dotnetclass "System.Text.RegularExpressions.Regex",

	keys = #(), 
	values = #(),
	fn ensure key exact:off =
	(
		if (k = finditem keys key) > 0 then k else 
		(
			k = keys.count+1
			if not exact do 
			(
				append keys key
				append values #()
			)
			k
		) 
	),
public 
	fn appendvalue key value =
	(
		append values[ensure key] value
	),
	fn get key exact:on = values[ensure key exact:exact],
	fn getkeys = keys,
	fn getvalues = values,
	fn count = keys.count,
	fn contains key = finditem keys key > 0,
		
	fn parse filename add:off = 
	(
		file = getfilenamefile filename
		if regex.ismatch file pattern do
		(
			d = regex.match file pattern
			s = TextureStruct file:filename base:d.groups.item[1].value type:d.groups.item[2].value
			if add do appendvalue s.base s
			s
		)
	),
	fn group files reset:off = 
	(
		if reset do 
		(
			keys = #()
			values = #()
		)
		for file in files where iskindof (s = parse file) this.TextureStruct do 
		(
			appendvalue s.base s
		)
	),
	on create do ()
)
	
/*
a = TexturesData()

(
	files = #()
	append files "wood-036_silver-fir-1_b.png"
	append files "wood-036_silver-fir-1_d.png"
	append files "wood-036_silver-fir-1_r.png"
	append files "wood-036_silver-fir-2_b.png"
	append files "wood-036_silver-fir-2_d.png"
	append files "wood-036_silver-fir-2_r.png"

	a.group files
)
a.getkeys()
a.getvalues()

for key in a.getkeys() do format "key:% >> %
" key (a.get key)
*/

the pattern “^(.*?)([b,d,r])$” means – ” i don’t care how it starts but has to end with “?” where “?” is any letter of ‘b’, ‘d’, or ‘r’ “