Jump to content

wcmatch within a List of Lists


Ross Dunkley

Recommended Posts

Hi, I have a list in the following format...  I guess its like a list within a list (sublist) (It's from Lee Mac's Update Titleblock routine if you're interested..!)

(("" "" "") ("" "Test" "") ("" "" "") ("" "" ""))

What I am trying to do is to return an element (sublist) that matches a particular criteria. I have been trying to do this with the following code I found online...

;; CAB 12/17/2009
;; returns a list matching items from the list 
(defun getMatching (pat lst / i mlst)
(mapcar
(function
(lambda (x)
(and (wcmatch x pat) (setq mlst (cons x mlst)))))
lst)
(reverse mlst))

If I try to test this I get the following... "Error: bad argument type: stringp ("" "" "")

(setq list1 '(("" "" "") ("" "Test" "") ("" "" "") ("" "" "")))

(defun c:test()
(print (getMatching "*Test*" list1))
(princ)
)

Regards.

Link to comment
Share on other sites

(defun getMatching (pat lst / i mlst)
  (mapcar
    (function
      (lambda (x)
        (mapcar
          '(lambda (s) (and (wcmatch s pat) (setq mlst (cons s mlst))))
          x
        )
      )
    )
    lst
  )
  (reverse mlst)
)

 

Link to comment
Share on other sites

Thanks Tharwat,

This returns the element 

("Test")

What I am trying to get is the entire sub-list (or list within the list) that matches the criteria. For example...

("" "Test" "")

This is because the actual list will have several elements that I need to work with. 

Would using vl-remove-if-not be the way to go? If so, how might this look?

Link to comment
Share on other sites

This?

(defun getMatching (pat lst / i mlst)
  (mapcar
    (function
      (lambda (x)
        (vl-some
          '(lambda (s) (and (wcmatch s pat) (setq mlst (cons x mlst))))
          x
        )
      )
    )
    lst
  )
  (reverse mlst)
)

 

  • Like 1
Link to comment
Share on other sites

FWIW -

;|
_$ (wcmatchList "test##" nil '(("test12")( "Test123" "re23" "asd") "32test" ("fd" "tEst55" "dsda")))
>> ("test12")
_$ (wcmatchList "test##" t '(("test12")( "Test123" "re23" "asd") "32test" ("fd" "tEst55" "dsda")))
>> ("test12" "tEst55")
|;
(defun wcmatchList ( pat case L / x c )
  (cond 
    ( (not L) L)
    ( (listp (setq x (car L)))
      (append (wcmatchList pat case x) (wcmatchList pat case (cdr L)))
    )
    ( 
      (append 
        (if 
          (progn 
            (setq c x)
            (if case (mapcar 'set '(x pat) (mapcar 'strcase (list x pat))))
            (wcmatch x pat)
          )
          (list c)
        ) 
        (wcmatchList pat case (cdr L))
      )
    )
  ); cond 
); defun wcmatchList

 

Link to comment
Share on other sites

If you wish to return the first match found:

(defun getmatch ( p l )
    (vl-some '(lambda ( x ) (if (vl-some '(lambda ( y ) (wcmatch y p)) x) x)) l)
)
_$ (getmatch "*Test*" '(("" "" "") ("" "TestA" "") ("" "" "TestB") ("TestC" "" "")))
("" "TestA" "")

If you wish to return all matches found:

(defun getmatches ( p l )
    (vl-remove-if-not '(lambda ( x ) (vl-some '(lambda ( y ) (wcmatch y p)) x)) l)
)
_$ (getmatches "*Test*" '(("" "" "") ("" "TestA" "") ("" "" "TestB") ("TestC" "" "")))
(("" "TestA" "") ("" "" "TestB") ("TestC" "" ""))

Aside @Tharwat: a relatively minor point, but if the list value returned by mapcar is not being used, in my opinion a foreach loop may be more applicable.

  • Thanks 1
Link to comment
Share on other sites

  • 5 months later...

Here is my adjustment of Lee Mac's "GetMatch" to allow searching for either text or numbers by using "Member". This works for me as the search term is always the first term in the list. Also strips our the search term from the result.

Lee, can you offer any advice on how to return the whole list if the search term is not the first term in the list? 

 

(defun getmatch              (SearchTerm ListToSearch)
  (vl-some '(lambda (x)
                     (member  SearchTerm x )
                   ) ;_ end of lambda
                  ListToSearch
  ) ;_ end of vl-some
) ;_ end of defun

Link to comment
Share on other sites

Hi Steve,

 

Assuming I've understood your intention, you could use either of the following (vl-position is typically faster than member)

(defun getmatch ( x l )
    (vl-some '(lambda ( y ) (if (member x y) y)) l)
)
(defun getmatch ( x l )
    (vl-some '(lambda ( y ) (if (vl-position x y) y)) l)
)

Note that your original code would have returned the list item including the search term.

Edited by Lee Mac
Link to comment
Share on other sites

Thank you Sir,

 

Your code always works well, and I have learned a lot from it.

 

Just so you know the purpose behind this effort, I have a list of tables with corresponding column widths. Each table is identified by either a string, an integer or a real ("840.STEEL", 841, 803.1). This lisp pulls the information for the table from a master list of lists that have the table id and column widths. I have another lisp that sets each table's column widths with the returned results. All in an effort to ensure conformity. Each of the tables gets fed by a linked excel sheet, which the Engineers and Architects are more comfortable editing. The link from excel initially sets the column width based on what is in the excel sheet, but the excel sheet has some difficulties being accurate due to how it bases cell width based on font character sizing. I also use your routine to strip out embedded format codes, so that the table style can control what is in the cells.

 

Yep, I was using a "Cdr" on the returned result, so I didn't see the search term in the result.

Like so

(SetQ result (cdr(getmatch "840.STEEL" MyList)))

or

(SetQ result (cdr(getmatch 803.1 MyList)))

SteveInAlaska

 

Link to comment
Share on other sites

I tried doing something similar for column widths and used STRLEN and a fuzzy factor, in my case looked at a list and got max strlen. It was not very reliable though.

Link to comment
Share on other sites

You're most welcome @Steve in Alaska, I'm delighted to hear that you could learn from my contributions.

 

FWIW, since you are essentially using a key to obtain associated information, assoc may be more applicable, e.g.:

(cdr (assoc "840.STEEL" MyList))

Though this of course requires that the key be the first element in the sublist.

Link to comment
Share on other sites

@Lee Mac, Yes, but I tend to make my lisp coding usable for more than one application, hence my interest in a solution that works for just about any way you would want to return a specific list from a "Master" list.

 

For what its worth, here is a small section of my code that creates the "Master lists of Table Columns", in reality there are a little over (50) different tables, some are identical.

 

(SetQ MyList (list
'(800 0.625 1 1 1 2.1875 2.1875)
'("800.AK" 0.625 1 1 1 3.1875 1.1875)
'(801 0.625 1 1 1 4.375)
'(802 0.625 1 1 1 1 1 2.375)
'(803 0.625 1.25 1.25 1.25 1.25 2.375)
                      ) ;_ end of list
) ;_ end of SetQ

 

And after separating out the column widths list for a specific table, I have this code (not yet finished, I need to chase down the parenthesis still and comment it better).

 

(Defun SetTblColWidth (TblType ListTblColWidth / CounterCol#)
;;; USAGE - (SetTblColWidth 800 '(0.6250   1.0000   1.0000   1.0000   2.1875   2.1875   ))
  (cond
    (                          ;cond1 
     (= (length ListTblColWidth) Number_COLS)
     (setQ CounterCol# 0)
     (repeat (length ListTblColWidth)
       (vla-setcolumnwidth TblObj CounterCol# (nth CounterCol# ListTblColWidth))
       (setQ CounterCol#
          (1+ CounterCol#)
       ) ;_ end of setQ
     )                          ;end cond1
     (                          ;cond2
      (/= Number_COLS Number_COLS)
       (alert (strcat "Columns mismatch, The Table Type "
              (rtos TblTypE 2 0)
              " actually has "

              " and the Table.LSP expects "
              (ITOA (length ListTblColWidth))
          ) ;_ end of strcat
       ) ;_ end of alert
     )                          ;end cond2
    )
  ) ;_ end of cond
) ;_ end of Defun

 

 

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...