4
\$\begingroup\$

I have a collection of full type names where I need to reduce common namespaces. For this purpose I wrote two extensions. The first one explodes each namespace into pieces and the second one removes the first name for as long as all first names are equal thus reducing each namespace.

static class Namespace
{
    public static IEnumerable<IEnumerable<string>> ExplodeNamespaces(this IEnumerable<string> namespaces)
    {
        return namespaces.Select(Split('.'));

        Func<string, IEnumerable<string>> Split(params char[] separators) 
            => s => s.Split(separators);
    }

    public static IEnumerable<IEnumerable<string>> ReduceNamespaces(this IEnumerable<IEnumerable<string>> namespaces)
    {
        while (IsFirstCommon(namespaces))
        {
            namespaces = namespaces.Select(SkipFirst());
        }

        return namespaces;

        bool IsFirstCommon<T>(IEnumerable<IEnumerable<T>> values) 
            => values.Select(ns => ns.FirstOrDefault()).Distinct().Count() == 1;

        Func<IEnumerable<string>, IEnumerable<string>> SkipFirst() 
            => values => values.Skip(1);
    }
}

Example:

var namespaces = new[]
{
    "foo.waldo.bar.baz.qux",
    "foo.waldo.bar.baz.quux",
    "foo.waldo.bar.baaz.qux",
    "foo.waldo.baar",
};

namespaces.ExplodeNamespaces().ReduceNamespaces().Dump();

Result:

bar 
baz 
qux 

bar 
baz 
quux 

bar 
baaz 
qux 

baar 
\$\endgroup\$
  • \$\begingroup\$ Apparently someone doesn't like namespaces :-P \$\endgroup\$ – t3chb0t Apr 7 '17 at 15:32
  • \$\begingroup\$ @Heslacher I think as long as they are pretty small it's ok but several lines might be too much indeed. I'm still experimenting so I don't know yet whether I (dis-)like them however I prefer to use a local function with a strong name rather then a magic expression, especially if it is related only to a single method. As soon as I need one in more then one place I promote it to a full member. \$\endgroup\$ – t3chb0t Apr 7 '17 at 16:06
  • \$\begingroup\$ IMO they are much harder to read, especially if you are using Expression bodies on method-like members \$\endgroup\$ – Heslacher Apr 7 '17 at 16:09
  • \$\begingroup\$ @Heslacher I usually try to stick to a single line and on a large monitor they don't look so weird but as soon as I need line breaks I use {}. I actually never use a formatting like in the question where the body is on a new line. I just put a line break there so that readers don't have to scroll horizontally so much. \$\endgroup\$ – t3chb0t Apr 7 '17 at 16:12
2
\$\begingroup\$

Those delegates inside methods are cool and everything, but I think you are overusing them a bit. For example, compare this:

    return namespaces.Select(Split('.'));

    Func<string, IEnumerable<string>> Split(params char[] separators) 
        => s => s.Split(separators);

to this:

return namespaces.Select(s => s.Split(new []{'.'}));

The second version is definitely way shorter and in my opinion is just as good (if not better), in terms of readability.


The algorithm looks alright to me. You might want to replace .Count() == 1 with, say, !....Skip(1).Any() to return earlier, but I don't think it matters for relatively small input.


It would be nice to also have a ReduceNamespaces(this IEnumerable<string> namespaces) method that would call namespaces.ExplodeNamespaces().ReduceNamespaces() internally. It will make probably the most common use case more accessible.

\$\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.