0

In our localization database we have the following objects:

public class Text
{
  public string Key { get; set; }
  public string LanguageId { get; set; }
  public string Value { get; set; }
}

I need a Linq statment to flatten these records to a structure that has the LanguageIds as a property with the values like:

public class ...
{
  public string Key { get; set; }
  public string En { get; set; }
  public string Ge { get; set; }
  public string Fr { get; set; }
  ...
}

Of course this can be achieved by some elaborate “for each”-ing but there must be a slik Linq statement that does the trick in one go. Any Link Gurus out there?

3
  • 2
    Do you mean to create a dynamic / ExpandoObject as the output, i.e. your output / projected class will have dynamically created properties? Commented Sep 3, 2012 at 8:19
  • @nonnb: It could be an anonymous object created by the select from the Linq Commented Sep 3, 2012 at 8:24
  • Could you specify more precisely what exact transformation should be done; the input and the output.....It is very difficult to think it over, reading your current question Commented Sep 3, 2012 at 8:28

4 Answers 4

4

I stumbled onto this and just wanted to add my solution to this question. I took the liberty to produce a small runnable example around the expression:

public class LocalizationManager
{
    public IDictionary<string, Localization> LostInTranslation()
    {
        var input = new[]
                        {
                            new Text {Key = "ORDINAL_1", LanguageId = "En", Value = "first"},
                            new Text {Key = "ORDINAL_1", LanguageId = "Fr", Value = "premier"},
                            new Text {Key = "ORDINAL_1", LanguageId = "De", Value = "erste"},
                            new Text {Key = "ORDINAL_2", LanguageId = "En", Value = "second"},
                            new Text {Key = "ORDINAL_2", LanguageId = "Fr", Value = "deuxième"},
                            new Text {Key = "ORDINAL_2", LanguageId = "De", Value = "zweite"},
                            new Text {Key = "ORDINAL_3", LanguageId = "En", Value = "third"},
                            new Text {Key = "ORDINAL_3", LanguageId = "Fr", Value = "troisième"},
                            new Text {Key = "ORDINAL_3", LanguageId = "De", Value = "dritte"},
                        };

        var output =
            input.GroupBy(text => text.Key).Select(
                group =>
                group.Aggregate(new Localization { Key = group.Key },
                                (translation, text) =>
                                    {
                                        translation.GetType().GetProperty(text.LanguageId).SetValue(translation, text.Value, null);
                                        return translation;
                                    }));

        return output.ToDictionary(key => key.Key);
    }
}

public class Text
{
    public string Key { get; set; }
    public string LanguageId { get; set; }
    public string Value { get; set; }
}

public class Localization
{
    public string Key { get; set; }
    public string En { get; set; }
    public string De { get; set; }
    public string Fr { get; set; }
}

Though this appears to be a slight perversion of Aggregate() I think it is true to its spirit. Please note that all languages have to exist as properties on the "Localization" object or you will get a NullReferenceException as GetProperty() returns null.

Sign up to request clarification or add additional context in comments.

Comments

3

Try the following:

var flattenTexts = texts
    .GroupBy(x => x.Key)
    .Select(x => new { Key = x.Key, 
                       De = x.First(y => y.LanguageId == "De").Value, 
                       En = x.First(y => y.LanguageId == "En").Value 
                     });

You will need to know which languages you have of course...

Comments

0

You can do something like:

typeof(MyClass).GetProperties().AsEnumerable().Select(x => new () {Key = x.Name, En = "English", Ge = "German (shouldn't it be De?)" ... }

2 Comments

Your right about the German Id, but I don't see yet how the Linq is going the produce the result I seek
I misread your question. I thought you wanted to load the values for different languages for different properties on the Text class. The answer @spontifixus is what I'd use.
0

If I understood what you mean you can create the second Object out of the 1st one and integrate this in your Linq statement for example:

  var result = TextObjectList.Select(x => new LanguageObject(x))

And in the LanguageObject Constructor set the values up to the LanguageId.

1 Comment

I's sure you can combine a result in the select to a new object, the question is how should the Linq be composed before the select: Joins, Unions, etc.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.