4
$\begingroup$

Trying to re-rwite for clarity. Referring to my previous lists: I would like to trim a dictionary so that all words that "consume" more than n letters "x" are deleted. So: Only words with 2 "a"'s and words with 1 "b" and words with 2 "d"'s etc. will be kept. According to this, "boffa" is deleted, but "bode" is kept.

The dictionary is large, so my lists below are just an illustration. One list is the dictionary, the other list contains the numbers for consumption on each and every word.

So I have three lists: "sa", "sb" and "sc". "sa" gives the number of usable characters for each word in "sc". Words can be of any size. List "sc"can only consist of letters in "sa".

List "sc" is the resulting list. The word "bobb" cannot exist in "sc" because it uses too many "b"'s. How to exclude words that consume too many letters?

sa = {"l", "a", "a", "b", "d", "d", "e", "g", "i", "o", "p", "s","v"}

sb = {"absid", "ad", "adagio", "basa", "be", "bebbe", "bebi", "bebis", "bebisapa", "bebo", "bebodd", "bebop", "bedagad", "beediga", "befogad", "begabba", "begiva", "beige", "beigea", "bes","bese", "bevis", "bevisa", "bi", "bibba", "bida", "biff", "biffa", "biff", "bifoga", "bio", "biogas", "bo", "boa", "bob", "bobb", "bod", "bodega", "boffa", "bog", "boggi", "bov" }`

The answer by @MichaelSeifert solves the answer nicely. Thank you everyone, what a generous and knowledgeable people you are!

$\endgroup$
  • $\begingroup$ The question is not particularly clear or easy to understand. You already seem to have an acceptable answer but you should still edit your question for clarity for the benefit of future visitors (so they can understand whether the answer is what they're looking for or not). To be clear, you're looking to select the words from listb whose characters are a subset of the ones in lista? From the stated listb, what is the expected output? You've stated that "carefree" is out, but which ones should it accept? $\endgroup$ – Emilio Pisanty Mar 2 '16 at 17:19
  • $\begingroup$ Also, presumably the items in listb should be strings? $\endgroup$ – Emilio Pisanty Mar 2 '16 at 17:19
  • 1
    $\begingroup$ If a character is not present in lista, should that be interpreted as "any number of this character is allowed", or "none of this character is allowed"? For some cases you seem to be saying the former (e.g., freedom is allowed even though lista doesn't contain an m), and for some you seem to be saying the latter (e.g., girl should not be allowed, even though none of its characters are in lista.) $\endgroup$ – Michael Seifert Mar 2 '16 at 18:27
  • $\begingroup$ I've revised my answer below to use the new sample data you've provided; let me know if it's still behaving differently than you expect. $\endgroup$ – Michael Seifert Mar 4 '16 at 14:46
  • $\begingroup$ @MichaelSeifert your answer is the solution and it works very well. Awesome. Every time it seems to be midnight when I get here but I wanted to thank you for taking the time. In more general terms, I am trying to get "all the words that can be done with a set of letters" without Combinatorics but rather to trim down a dictionary. It works quite well now. Unfortunately I am working with Swedish characters (å, ä, ö) and I do not get them correctly into Mathematica. But that will be another question. Thank you very much! I will up vote your answer now. $\endgroup$ – JSP Mar 4 '16 at 23:04
5
$\begingroup$
selectF = With[{sa = #, sb = #2}, 
   Function[{x}, Max[Subtract @@ (StringCount[{x, StringJoin[sb]}, #]) & /@ 
        Intersection[Characters[x], sb]] <= 0] /@ sa] &;

Pick[listb, selectF[listb, lista]]

{"abc", "freedom", "math"}

$\endgroup$
  • $\begingroup$ Thank you very much @kglr. It really works nicely as stated. But if I add a "girl" in list b, she will be added to the final result, although there was no letter "g" to use (spend) in lista. In my case, lista a has around 100 k words (I should have mentioned it). $\endgroup$ – JSP Mar 2 '16 at 15:42
  • $\begingroup$ @JSP, you might want to wait a while before accepting an answer. This question is likely to attract many more answers. Regarding "girl" i probably got te logic wrong --i thought it should be in the final result, just like "math". Perhaps, you want an additional condition like Intersection[Characters[x], lista] !={} for x to be in the result? $\endgroup$ – kglr Mar 2 '16 at 16:03
  • $\begingroup$ My fault, all the words should be made up of the characters in lista, yes. So "freedom" is a bad example (I was thinking about the number of "e"'s). I will run through the suggestions now, thank you @kglr and @Michael Seifert $\endgroup$ – JSP Mar 2 '16 at 20:31
  • $\begingroup$ I have tried both suggestions by @kglr and @MichaelSeifert. Both yield the same result but they are not correct all the way. They let some letters pass through, "b"and "g"seem especially erroneous. $\endgroup$ – JSP Mar 2 '16 at 23:19
  • $\begingroup$ @kgIr, I posted two much more realistic sample lists above. Running your suggestion with those, I find that it does some good work but lets words with "b"slip through. The output becomes: {"absid", "ad", "adagio", "basa", "be", "bedagad", "befogad", "begiva", "bes", "bevis", "bevisa", "bi", "bida", "biff", "biffa", "biff", "bifoga", "bio", "biogas", "bo", "boa", "bod", "bodega", "boffa", "bog", "bov"} All words with ”f” are definitely wrong. Cannot understand why. Maybe you have some additional comment? Thank you very much. $\endgroup$ – JSP Mar 3 '16 at 22:14
1
$\begingroup$

(edited to use new sample data)

If only the characters in lista are to be used, up to their multiplicities, then the following code should work:

lista = {"l", "a", "a", "b", "d", "d", "e", "g", "i", "o", "p", "s", "v"};
listb = {"absid", "ad", "adagio", "basa", "be", "bebbe", "bebi", "bebis", "bebisapa", "bebo", "bebodd", "bebop", "bedagad", "beediga", "befogad", "begabba", "begiva", "beige", "beigea", "bes", "bese", "bevis", "bevisa", "bi", "bibba", "bida", "biff", "biffa", "biff", "bifoga", "bio", "biogas", "bo", "boa", "bob", "bobb", "bod", "bodega", "boffa", "bog", "boggi", "bov"};
alphabet = CharacterRange["a", "z"]
chartests[word_] := (Count[lista, #] >= Count[Characters[word], #]) & /@  alphabet;
Select[listb, (And @@ chartests[#])&]

(* {"absid", "ad", "adagio", "basa", "be", "bedagad", "begiva", "bes", "bevis", "bevisa", "bi", "bida", "bio", "biogas", "bo", "boa", "bod", "bodega", "bog", "bov"} *)

(The code below was only applicable before the question was clarified, and does not answer the question as it now stands. I am including it for historical interest only.)

If you only want to limit the characters that are present in lista, and allow for any other character to appear an arbitrary number of times, use the following code instead:

lista = {"a", "e", "e", "d", "f"};
listb = {"abc", "caesar", "freedom", "carefree", "math"};
alphabet = CharacterRange["a", "z"]
chartests[word_] := (Count[lista, #] == 0 || Count[lista, #] >= Count[Characters[word], #]) & /@ alphabet;
Select[listb, (And @@ chartests[#]) &]

(* {"abc", "freedom", "math"} *)

Note that this will return True for "girl" as well; as I noted in the comments above, your question is a bit ambiguous as to whether this should be included or not.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.