Generating different combinations from a list of muscles

Suppose we have a model of a human muscular system containing the following muscles each within a specific muscle group:

  • Arms
    • Biceps
    • Triceps
    • Forearms
  • Torso Front
    • Abs
    • Chest
    • Shoulders
  • Torso Back
    • Trapezius
    • Lats
    • Rear Shoulders
    • Lower Back
  • Lower Body
    • Quads
    • Hams
    • Hip & Butt
    • Calves

Now, given a list of selected muscles, I need to find combinations following the given rules:

If all muscles are selected from a group:

Full body (All muscles) 
Arms 
Torso
Torso Front
Torso Back
Lower Body

If there are full groups together:

Arms and torso
Arms and torso front
Arms and torso back
Arms and lower body
Torso and lower body
Torso front and lower body
Torso back and lower body

When both the front and back of the torso is selected it can be considered just “torso”.

If there are more than 2 muscles selected from a group but not all:

Parts of torso (This is the case where there are muscles selected from both front- and back-torso)
Parts of torso back
Parts of lower body

“Parts of torso front” and “Parts of arms” never occur on their own as they only have a total of 3 muscles, thus those cases can be represented by their names: eg. “biceps and triceps”, etc.

Combinations of these aforementioned “parts”

Parts of arms and torso
Parts of arms and lower body
Parts of torso and lower body
Parts of  arms, torso and lower body.

Parts = groups are not full and there are more than 2 muscles selected. This “rule of 2” is the most important one.


Examples of output

biceps, triceps and chest: parts of arms and torso

biceps, triceps, calves, tai biceps, triceps, quads, hams, hip & butt: Parts of arms and lower body

biceps, abs, calves: Parts of arms, torso and lower body

biceps, triceps: Biceps and triceps

biceps, triceps, forearms: Arms

chest, shoulders, abs, trapezius, lats, rear shoulders, lower back: Torso

and so on, there are multiple possible variations.


Now my problem is that I have no idea how to write this as clean and modular as possible, so that rules can be added or removed if necessary.

I could just write a massive if-elseif-else pile that would consider all cases, but you know why I won’t.

How would you tackle this kind of task?

4

  • Create a class “MuscleGroup”
  • Init each object of that class with the list of muscles belonging to that class, and the name of the group
  • add a method PrintRelatedParts(SetOfMuscles som) to that class (SetOfMuscles should be some container type for the given input)
  • the method should count how many of the given muscles in som are in the muscle group. If there are 0, it prints nothing, if there are 1 or 2, it prints the names of the muscles, if there are more than 2, it prints “parts of …”, and if there are all muscles of that group, it prints just the name of the group.
  • add some logic to combine the output of all muscle group objects with “and” and “,”

The essential decision you have to program here is just a decision between 4 cases, not a “massive if-elseif-else pile”.

5

I would not think too much about making it extensible. Human bodies are not going to change in a foreseeable future. I would model the data structures after who the users are. Are they chiropractors? Physical therapists? Gym rats? A few conditionals in one place is often better than introducing two design patterns and a handful of classes.

2

I hate generalizing examples like this because you are always guessing and your brilliant generalization might get destroyed by the new rule they come up with. So if possible ask the domain experts if your abstraction makes sense to them.

That being said, it sounds like you have a hierarchy. It’s mostly obvious from what you list, plus torso is a parent of front torso and back torso. Each node in the hierarchy can describe itself with a string. Your leaf nodes just print their name (e.g. “biceps”). Most of your interior nodes follow your “rule of 2” (either combining the string values of the children or replacing with another value as described in the question). The torso node basically follows a “rule of 1”, using the child’s input only if there is exactly 1 child. There is an implicit root node (“whole body node”?) that handles the “and” and “,” logic.

You might have 4 different node classes here, or a strategy pattern, or 3 strategies one of which takes the “rule of X” parameter.

This records a bit more semantic information and might generalize better – until you find out that a finger and a toe combine into “digits” even though they are otherwise in a completely different branch of the hierarchy. But I would probably do this over Doc Brown’s simpler solution if I believed there would be many more such cases in the future and the domain experts thought they would fit this model.

EDIT The following is probably what Doc Brown was already suggesting, except that I’m longwinded and make a lot of fuss about the set abstraction.

I’m not sure object oriented patterns are the right approach here. I think the main abstraction you need is simply a set.

Your primitive elements are “biceps”, “triceps” etc. Your “universal” set is the complete set of those elements. “arms” and “torso front” are just sets – subsets of the universal set.

For your more complex criteria, you’d need a reasonable set of operations on sets – union, intersection, difference, counting elements etc. There are design patterns that you could use to specify functions based on those, but I would think “Abstract Syntax Tree” rather than “GOF pattern”, and even that is probably overkill.

All you really need to define all your classification rules is a sequence (list, array, … – maybe even a set) of records (or objects). The classification test itself can just be whatever representation of a function is most convenient in your language – maybe literally a function or lambda, maybe some kind of delegate. Accept the set of primitive muscles as a parameter and return true/false. That function is then just a field of each record.

You could define a short fat class hierarchy – a base class with a pure “test” method and a derived class for each classification test – but this seems like overkill to me. If you can include functions (or function pointers) in records, just include a field to hold the function. A more complex class hierarchy, attempting to define the muscle-set relationships within the class relationships, would just be OOP for it’s own sake IMO – lot’s of code to write for little or no benefit.

You might define a tree, but that would be a data structure rather than a class hierarchy, and even then only for optimization if you’re dealing with a lot of muscles. If you’re really dealing with a lot more than 14 muscles, you may even have some automatic tree-building logic. That would probably be based on a partial order (topological sort) based on comparing sets of combinations of primitive muscles, with is-subset-of as the ordering function. And that could be a fair bit of work…

  • The result wouldn’t naturally form a tree – a small set may be a subset of many different larger sets – so there’s definitely issues to work out. Either you make some arbitrary choices, or you build a DAG (Directed Acyclic Graph) rather than a tree.

  • The sets are of combinations, not muscles. Either you deal with a combinatorial explosion, or you need to deal with specifications of sets and classification tests in a more sophisticated way.

IOW, don’t waste time on it unless you’re sure you really need it.

Depending on how you use this precisely, the results may be a set too – a set of classifications that apply rather than a set of muscles. Each member of that would refer to one of your classification tests – depending on language and other issues, maybe by some kind of ID rather than by a pointer/reference to the record/object.

2

Here, briefly in Java :

if may be replaced by the Observable/Observer Pattern, more useful, stable and secure.

A type is defined in enum, each typeName of refer to an Observer, the ordinal() position in enum give the bit of the type [ordinalBitOfType].

A muscle have a set of one or more ordinalBitOfType

The Observable have a HashSet of ordinalBitOfType to request: enumType.ARMS.ordinal() and enumType.TORSO.ordinal()

Then it activates observers. All concerned Observers print “hello” and can update Map or something else in the Observable, others do nothing.

EDIT
In Observable/Observer Pattern only one if by Observer is needed to respond at : is myBit contained in the Observable.Set?

If you create New types, you have to create new Obervers, don’t worry about already Observers jobs, they will work fine, just put the new bits in the right muscles.

You can had Observable Pattern in a Observer for nested if.
Take care, you cannot decide the order of the Observers called by the JVM.

EDIT 2 : some rewriting + O’Reilly book

The clearest book about Pattern design I’ve read : “Head First Design Patterns”, encourage to master it.

EDIT 3

I open a second answer because I realize I was blinded by the if/then/else problem, and also because Observable instance passed to an Observer which can modify it, is a good pattern for limited and stable parameters to manage intricated ifs.

4

Answer 2 :

The real problem is to link body’s elements together.

For this case we have only two types : muscles and bones [here, grouped in body part].
So if we begin to develop group (of bones) we certainly will be able to detail them to bones granularity.

It is obvious that add a new group of nerves will not be difficult if link model between bones and muscles is adequate.

I’m thinking in Java with OODB to give sample.
I use the OpenSource db4objects, it is also Java, possibility to use EmbeddedObjectContainer loaded with the API, no server, no DBA’s problem.
Think it like a disk with useful database tools to manage complex I/O like Transparent Activation/Transparent Persistence, and other powerful tools like a manager, and it is able to migrate to huge Versant professional OODB.

Your job : work with a unique class with three fields :

package ElemBody;

import java.util.HashSet;
import java.util.Set;

public final class ElementBody {

    // In true life fields are 'private' with getters and setters
    public int typ; // TYP_ELEM.BONES.ordinale() Indexed
    public String name; // indexed with UniqueKey
    // HashSet (and not SortedSet for optimization) Element linked
    public Set<ElementBody> linkedElem = new HashSet<ElementBody>();

    // Print utility    
    @Override
    public final String toString() {
        final StringBuilder sb = new StringBuilder();
        sb.append(name);
        sb.append(" - ");
        for (final ElementBody el : linkedElem) {
            sb.append(el.name);
            sb.append(", ");
        }
        sb.append(linkedElem.size());
        sb.append(".");
        return sb.toString();
    }
}

The main, (tedious) loading/linking data and quering :

package ElemBody;

import java.io.File;
import java.util.Iterator;

import com.db4o.Db4oEmbedded;
import com.db4o.EmbeddedObjectContainer;
import com.db4o.ObjectSet;
import com.db4o.config.EmbeddedConfiguration;
import com.db4o.constraints.UniqueFieldValueConstraint;
import com.db4o.query.Query;

public class PrmBody {
    public enum TYP_ELEM {
        BONES, MUSCLES, NERVES // here, place for futur types
    }

    public static void main(final String[] args) {
        // one of possible db4o's configuration
        final EmbeddedConfiguration config = Db4oEmbedded.newConfiguration();
        // Managing index and unique key
        config.common().objectClass(ElementBody.class).objectField("type")
            .indexed(true);
        config.common().objectClass(ElementBody.class).objectField("name")
            .indexed(true);
        config.common().add(
            new UniqueFieldValueConstraint(ElementBody.class, "name"));
        // You have also an InMemory option, here the file one
        // Because of name's uniqueKey option, delete for test, in true live
        // read before create/update
        new File("Q:/tmp/ElementBody.db4o").delete();
        final EmbeddedObjectContainer eoc = Db4oEmbedded.openFile(config,
        // "ElementBody.db4o"); // in "user.home" directory
            "Q:/tmp/ElementBody.db4o");

        // Tedious option to load database.
        // Use .properties ou CSV files

        // Create BONES
        for (final String s : new String[]{"Arms", "Torso Front", "Torso Back",
            "LowerBody"}) {
            final ElementBody el = new ElementBody();
            el.typ = TYP_ELEM.BONES.ordinal();
            el.name = s;
            eoc.store(el); // save new instance
        }

        // Create MUSCLES
        final String[] muscles = {"Biceps", "Triceps", "Forearms",//
            "Abs", "Chest", "Shoulders", //
            "Trapezius", "Lats", "Rear Shoulders", "Lower Back", //
            "Quads", "Hams", "Hip & Butt", "Calves"};
        for (final String s : muscles) {
            final ElementBody el = new ElementBody();
            el.typ = TYP_ELEM.MUSCLES.ordinal();
            el.name = s;
            eoc.store(el);
        }

        // Simplified control of read type form db4o
        final Query queryA = eoc.query();
        queryA.constrain(ElementBody.class);
        queryA.descend("name").constrain("Arms").equal();
        final ElementBody arm = (ElementBody) queryA.execute().iterator()
            .next();
        for (final String s : new String[]{"Biceps", "Triceps", "Forearms",
            "Shoulders"}) {
            final Query query = eoc.query();
            query.constrain(ElementBody.class);
            query.descend("name").constrain(s).equal();
            final ObjectSet<ElementBody> musT = query.execute();
            musT.get(0).linkedElem.add(arm);
            arm.linkedElem.add(musT.get(0));
            eoc.store(musT.get(0));
        }
        eoc.store(arm);

        final Query queryB = eoc.query();
        queryB.constrain(ElementBody.class);
        queryB.descend("name").constrain("Torso Front").equal();
        final ElementBody tf = (ElementBody) queryB.execute().get(0);
        for (final String s : new String[]{"Abs", "Chest", "Shoulders"}) {
            final Query query = eoc.query();
            query.constrain(ElementBody.class);
            query.descend("name").constrain(s).equal();
            final ElementBody mus = (ElementBody) query.execute().get(0);
            mus.linkedElem.add(tf);
            tf.linkedElem.add(mus);
            eoc.store(mus);
        }
        eoc.store(tf);

        final Query queryC = eoc.query();
        queryC.constrain(ElementBody.class);
        queryC.descend("name").constrain("Torso Back").equal();
        final ElementBody tb = (ElementBody) queryC.execute().get(0);
        for (final String s : new String[]{"Trapezius", "Lats",
            "Rear Shoulders", "Lower Back", "Shoulders"}) {
            final Query query = eoc.query();
            query.constrain(ElementBody.class);
            query.descend("name").constrain(s).equal();
            final ElementBody mus = (ElementBody) query.execute().get(0);
            mus.linkedElem.add(tb);
            tb.linkedElem.add(mus);
            eoc.store(mus);
        }
        eoc.store(tb);
        eoc.commit(); // End load part // // //

        // Two samples of request
        final Query r1 = eoc.query();
        r1.constrain(ElementBody.class);
        r1.descend("typ").constrain(TYP_ELEM.BONES.ordinal()).equal();
        for (final Object obj : r1.execute()) {
            final ElementBody el = (ElementBody) obj;
            System.out.println("n" + TYP_ELEM.BONES + "t" + el.name);
            final Iterator<ElementBody> it = el.linkedElem.iterator();
            while (it.hasNext()) {
                System.out.println("t" + it.next());
            }
        }

        final Query r2 = eoc.query();
        r2.constrain(ElementBody.class);
        r2.descend("typ").constrain(TYP_ELEM.MUSCLES.ordinal()).equal();
        r2.descend("name").constrain("Q").smaller();
        r2.descend("name").orderAscending();

        final ObjectSet<ElementBody> osEl = r2.execute();
        System.out.println("---------------n" + osEl.size()
            + " muscles in db with A to P prime letter name.");
        final Iterator<ElementBody> itEl = osEl.iterator();
        int sumLink = 0;
        while (itEl.hasNext()) {
            final ElementBody elTmp = itEl.next();
            System.out.print(elTmp.name + ", ");
            sumLink += elTmp.linkedElem.size();
        }
        System.out.println("nThose muscles have " + sumLink
            + " links to bones, LowerBody's ones excluded.");
        eoc.close();
    }
}

I’ve add “Shoulders” in two ‘Torso’ to show how you can feed back your links.

With samples of (SODA) query, you will be able to pick all needed groups.

Console:

BONES   Arms
    Forearms - Arms, 1.
    Triceps - Arms, 1.
    Shoulders - Arms, Torso Back, Torso Front, 3.
    Biceps - Arms, 1.

BONES   Torso Front
    Abs - Torso Front, 1.
    Shoulders - Arms, Torso Back, Torso Front, 3.
    Chest - Torso Front, 1.

BONES   Torso Back
    Lower Back - Torso Back, 1.
    Trapezius - Torso Back, 1.
    Rear Shoulders - Torso Back, 1.
    Shoulders - Arms, Torso Back, Torso Front, 3.
    Lats - Torso Back, 1.

BONES   LowerBody
---------------
9 muscles in db with A to P prime letter name.
Abs, Biceps, Calves, Chest, Forearms, Hams, Hip & Butt, Lats, Lower Back, 
Those muscles have 6 links to bones, LowerBody's ones excluded.

In my sense, it is not a mathematical model, but an intuitive one :

  • what is the real question? → where are real objects? (do not begin with abstraction, or technical solution, if no needed, and decrypt the real problem, behind the beautiful requirements paper)
  • who really works? → what is done? (take the place of the objects, then imagine how you are now represented : hum, not so good representation doesn’t it?)
  • who and what is really needed (a lazy developer want to verify/test less possible objects and more simple objects than an ‘all home made’ one)
  • we manage data? → a database is needed,
  • can we be more simple? → this take time, a night is generally a good elapse time
  • is request the only problem for final user? → right! look him working, he is the only men able to understand what he want, here the problem : experience will show you how decrypt those ‘not said but critical’ things.

If all [or part] of this is already made → use it

Unless it is for highly stress usage (or OS only known by one genius) all is already written, and often well written and optimized, and secure.

Here I’ve used something like ‘internal reverse index model’ (Perhaps exists under another official name) :
Database have indexes on fields (columns for SGBDRs) as all database you know
In OODB we add list/set/BTree/.. to link an instance of an object type to another one of same type, or to another instance of a different type (but always the same instances type linked in one list).

If your project came bigger and more complex you will be able to use such methods without difficulties.
No complexity to load numerous CSV files, no real difficulties to add queries,

  • One class, three fields, and basic methods (your job, already made, only add query for an object before create or update if you do not want delete database each tim – and other pitfall to avoid, listen in reference doc)
  • One OODB (imported) (just configure it, add TA/TP, you can put it inMemory)
  • One class for CSV file (import CsvReader)
  • One class to load Csv in DB (Little job, model in PrmBody)
  • Some simple classes for dedicated queries (not painfull if you understand SODA query)
  • I use JSON, but take your prefered web tool for presentation (may be time eater for all public usage).

Feel free to tell us if it convenient for you.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật