Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I make a new list of strings from another list based on certain criteria?

Tags:

c#

algorithm

That's the best way I could think to ask the question, the detail is below. It's taken me an hour just to figure out how to ask the question!

Let's say that I have 5 (or more) types of text files - they are generated by a scientific instrument and each has certain results. Let's call these "types" A, B, C, D, E. The actual names of the text files don't give this away so the user can't easily see what they are just by the name. If it makes it easier, we could just consider a list of "strings". { I have NO idea what to do about replcate types but I'll worry about that later)

I wish to give the user the option to combine the text files into a conglomerate file but the challenge is, it doesn't make sense to combine certain types (for reasons that I don't feel it worthwhile to go into).

I've constructed and example matrix of compatibility

        A   B   C   D   E
   A    1   1   1   1   0
   B    1   1   1   0   0
   C    1   1   1   1   0
   D    1   0   1   1   1
   E    0   0   0   1   1

So, this is saying that I could combine A with B, C, D but not E (and so on).

Now if a user chooses files that have types "A, B and C" from a list of files (that aren't obviously typed) I would like to check the selections and say "yes, ABC is a legal merge" and perform the merge.

If the user selects A,B,C,D, I would like to say, NO, you can't do that as D is not compatible with B - however, you could do A,B,C or A,C,D (as D should not be where B would be).

Still with me?

I created the above matrix in code and have got to a looping structure where I started to get a bit muddled and thought I might ask for some help before I find myself losing my home and family. I've got a fairly large comment section on how I'm trying to make choices. Remember that "A" could be "Dog" and "B" could be "Cat" etc.

internal class CompatibilityCheck
{
    private List<string> FileTypes;

    public CompatibilityCheck()
    {
        //Strings are unique types for text files
        FileTypes = new List<string> {"A", "B", "C", "D", "E"};
        int[,] CompatibilityMatrix =
            {
                {1, 1, 1, 1, 0},
                {1, 1, 1, 0, 0},
                {1, 1, 1, 1, 0},
                {1, 0, 1, 1, 1},
                {0, 0, 0, 1, 1}
            };

        /* Scenario 1 */
        //List of file names from user = ALL LEGAL
        var userSelection = new List<string> {"A", "B", "C"};
        /* Go through each item in userSelection and make arrays of "legal" file combinations?
         * start with A and find the compatible matches, B and C.  Check that B and C are okay. 
        * Answer should look like this as A, B and C can be combined */

        /* Scenario 2 */
        // List of file names from user = NOT ALL LEGAL => D and B are incompatible
        var userSelection2 = new List<string> {"A", "B", "C", "D"};
        /* In my head, I go through each item in userSelction2
         * Take A and build "A,B,C,D" as it is compatible
         *  check B with C(yes) and D(no) - remove D.
         *      end [ A,B,C ]
         *          
         * Start with B and build B,A,C
         *  check A with C(yes) 
         *      end [ B,A,C ]
         *          
         * Start with C and build C,A,B
         *  check A with B(yes)
         *      end [C,A,B]
         *      
         * Start with D and build D,A,C
         *  check A with C(yes)
         *      end [D,A,C]
         *      
         * take the nth string and compare to n+1, n+2 ... n+(length-n)
         *  
         * the unique string sets woudld be  A , B , C and A , C , D  
         */
    }
}

I hope this is clear. So What is the best way to achieve such a task? Recursion, LINQ "Magic", matrix math?

[EDIT:] An idea I just had that may be easier to implement, might be to Show a list of files and as a user selects them, I could "deactivate" the other choices that aren't compatible. Imagine a listBox or similar where you select an "A" type and if the above matrix is in play, type "E" files would be greyed out. I wonder if this will cause me less stress ...?

like image 274
Sisyphus Avatar asked Oct 31 '12 10:10

Sisyphus


People also ask

How do you split a list based on a condition in Python?

split() , to split the list into an ordered collection of consecutive sub-lists. E.g. split([1,2,3,4,5,3,6], 3) -> ([1,2],[4,5],[6]) , as opposed to dividing a list's elements by category. Discussion of the same topic on python-list. IMAGE_TYPES should be a set instead of a tuple: IMAGE_TYPES = set('.

How do you replace a string in a list with another string?

Replace a specific string in a list. If you want to replace the string of elements of a list, use the string method replace() for each element with the list comprehension. If there is no string to be replaced, applying replace() will not change it, so you don't need to select an element with if condition .


1 Answers

I haven't had much time to look at this or refactor my code so this is more of a brain dump but might be a good place to start and refactor into something a little neater!

public class StrongFileType
{
    private string _friendlyName = string.Empty;

    public StrongFileType(string friendlyName)
    {
        _friendlyName = friendlyName;
    }

    public IEnumerable<StrongFileType> CompatibleTypes { get; set; }

    public override string ToString()
    {
        return _friendlyName;
    }
}

private void SampleTest()
{
    // The possible types
    var typeA = new StrongFileType("A");
    var typeB = new StrongFileType("B");
    var typeC = new StrongFileType("C");
    var typeD = new StrongFileType("D");
    var typeE = new StrongFileType("E");

    // Setup possible compatible types
    typeA.CompatibleTypes = new List<StrongFileType> { typeA, typeB, typeC, typeD };
    typeB.CompatibleTypes = new List<StrongFileType> { typeA, typeB, typeC };
    typeC.CompatibleTypes = new List<StrongFileType> { typeA, typeB, typeC, typeD };
    typeD.CompatibleTypes = new List<StrongFileType> { typeA, typeC, typeD, typeE };
    typeE.CompatibleTypes = new List<StrongFileType> { typeD, typeE };

    // Now do a check...
    var userSubmittedFilesValid = new List<StrongFileType> { typeA, typeB, typeC };
    CheckCompatible(userSubmittedFilesValid);
    var userSubmittedFilesInvalid = new List<StrongFileType> { typeA, typeB, typeC, typeD };
    CheckCompatible(userSubmittedFilesInvalid);
}

private bool CheckCompatible(IEnumerable<StrongFileType> requestedFiles)
{
    // Useful for debugging
    var validList = new List<string>();
    var invalidList = new List<string>();

    foreach (StrongFileType fileType in requestedFiles)
    {
        string invalid = string.Empty;
        string validCombination = string.Empty;

        foreach (StrongFileType fileTypeToCheck in requestedFiles)
        {
            if (!fileType.CompatibleTypes.Contains(fileTypeToCheck))
            {
                // Show as not compatible and remove any previously valid combinations that match
                invalid += string.Format("{0} not compatible with {1}", fileType, fileTypeToCheck);
                validList.RemoveAll(x => x.Contains(fileType.ToString()) && x.Contains(fileTypeToCheck.ToString()));
            }
            else
            {
                validCombination += string.Format("{0}", fileTypeToCheck);
            }
        }

        // Add to respective lists
        if (!string.IsNullOrEmpty(validCombination) && !validList.Contains(validCombination))
        {
            validList.Add(validCombination);
        }
        if (!string.IsNullOrEmpty(invalid))
        {
            invalidList.Add(invalid);
        }
    }

    // Was valid?
    return invalidList.Count == 0;
}

This should result with the first one showing VALID list of ABC and INVALID list is empty. Second one should show ABC and ACD as VALID and BD and DB as INVALID.

Sorry I didn't have more time to tidy it up!

like image 173
Belogix Avatar answered Oct 11 '22 08:10

Belogix