Alex
Alex

Reputation: 73

Concatenate string property of a custom object from json array

Here is a brief description of the problem. From server I get json array which looks similar to this:

    [
  {
    "messagenumber": "0529069f-a403-4eea-a955-430a10995745",
    "message": "A",
    "partnumber": 1,
    "total": 3
  },
  {
    "messagenumber": "0529069f-a403-4eea-a955-430a10995745",
    "message": "B",
    "partnumber": 2,
    "total": 3
  },
  {
    "messagenumber": "0529069f-a403-4eea-a955-430a10995745",
    "message": "C",
    "partnumber": 3,
    "total": 3
  },
  {
    "messagenumber": "52e7d68d-462b-46b9-8eec-f289bcdf7b06",
    "message": "AA",
    "partnumber": 1,
    "total": 2
  }......
]

It represents the list of messages parts. Message parts that belong to the same group are given the same messagenumber, and partnumber represents the order number. On the client I have to, at the end, combine all message parts belonging to the same group and render full message. From example above, result would look like this for individual message:

var msg = new FullMessage();
msg.MessageNumber = "0529069f-a403-4eea-a955-430a10995745";
msg.Message = "ABC"; //here we have full message created out of 3 parts
msg.PartNumber = 1; //not important 
msg.Total = 1 //also not important

and the final result should be a collection of FullMessage objects. Also, messages that are partial (not all parts are present) should be ignored.

Can this be done elegantly using LINQ statement(s) ?

Thank you for any suggestion.

Upvotes: 1

Views: 500

Answers (5)

Aleks Andreev
Aleks Andreev

Reputation: 7054

Try this code:

var input = new List<FullMessage>();

var result = input.GroupBy(m => new { m.MessageNumber, m.Total })
    .Where(g => g.Count() == g.Key.Total)
    .Select(g => new FullMessage
    {
        MessageNumber = g.Key.MessageNumber,
        Message = g.OrderBy(i => i.PartNumber).Aggregate("", (c, n) => c + n.Message)
    });

Upvotes: 0

steliosbl
steliosbl

Reputation: 8921

This should do what you want:

var myList = new List<FullMessage>();
myList.GroupBy(msg => msg.MessageNumber)
      .Where(grouping => grouping.Count() == grouping.First().Total)
      .Select(grouping => grouping.OrderBy(x => x.order))
      .Select(grouping => grouping.Select(x => x.Message)
      .Aggregate((x, y) => x + y)); 

What this does is the following:

  1. Group messages by their MessageNumber
  2. Filter out all partial messages
  3. Order all messages of each grouping by PartNumber to ensure everything is in the correct order
  4. Go through all groupings and select all messages
  5. Aggregate messages

The result is an enumeration of concatenated strings.

Upvotes: 1

James Curran
James Curran

Reputation: 103535

Here's mine: (UPDATED to remove partials)

var messages = new Message[] {
    new Message("52e7d68d-462b-46b9-8eec-f289bcdf7b06", "BB", 2, 2),
    new Message("0529069f-a403-4eea-a955-430a10995745", "A", 1, 3),
    new Message("0529069f-a403-4eea-a955-430a10995745", "B", 2, 3),
    new Message("0529069f-a403-4eea-a955-430a10995745", "C", 3, 3),
    new Message("52e7d68d-462b-46b9-8eec-f289bcdf7b06", "AA", 1, 2),
};

    var grouped = (from m in messages
                  group m by m.messagenumber into g
                  select new
                  {
                    messagenumber = g.Key,
                    message = String.Join("", g.OrderBy(dr =>dr.partnumber)
                                               .Select(m=>m.message)),
                    count = g.Count(),
                    total = g.First().total
                  }).Where(c=>c.count == c.total);

Upvotes: 0

Fruchtzwerg
Fruchtzwerg

Reputation: 11399

Let's say the message looks like

public class FullMessage
{
    public string MessageNumber { get; set; }
    public string Message { get; set; }
    public int PartNumber { get; set; }
    public int Total { get; set; }
}

Here is the idea of my possible solution:

  1. Group by the message number
  2. Order each messages in the groups by their part number
  3. Combine the message parts and count up the total (not sure if you need to count up so this is optional) in a new FullMessage object using Aggregate

The result is this LINQ query:

IEnumerable<FullMessage> combinedMessages = messages.GroupBy(m => m.MessageNumber)
    .Select(g => g.OrderBy(m => m.PartNumber)
    .Aggregate(new FullMessage() { MessageNumber = g.Key }, (r, m) =>
    {
        r.Message += m.Message;
        //Not sure if counting up is required in your code
        //so the following line is optional
        r.Total += m.Total;
        return r;
    }));

Upvotes: 0

David Hor&#225;k
David Hor&#225;k

Reputation: 5565

An example code below:

void Main()
{
    // Testing mocked data
    var data = new List<ServerMessagePart> {
        new ServerMessagePart{
            Messagenumber = "0529069f-a403-4eea-a955-430a10995745",
            Message= "A",
            Partnumber= 1,
            Total = 3
        },
        new ServerMessagePart{
            Messagenumber = "0529069f-a403-4eea-a955-430a10995745",
            Message= "C",
            Partnumber= 3,
            Total = 3
        },
        new ServerMessagePart{
            Messagenumber = "0529069f-a403-4eea-a955-430a10995745",
            Message= "B",
            Partnumber= 2,
            Total = 3
        },
        new ServerMessagePart{
            Messagenumber = "52e7d68d-462b-46b9-8eec-f289bcdf7b06",
            Message= "AA",
            Partnumber= 1,
            Total = 2
        }
    };

    // Compiled messages
    var messages = new List<ServerMessage>();

    // Process server partial messages - group parsts by message number
    var partialMessages = data.GroupBy(x => x.Messagenumber);
    foreach(var messageParts in partialMessages) {

        // Get expected parts number
        var expected = messageParts.Max(x=>x.Total);

        // Skip incompleted messages
        if(messageParts.Count() < expected) {
            continue;
        }

        // Sort messages and compile the message
        var message = messageParts.OrderBy(x => x.Partnumber).Select(x => x.Message).Aggregate((x,y) => x + y);

        // Final message
        messages.Add(new ServerMessage{
            Messagenumber = messageParts.First().Messagenumber,
            Message = message
        });
    }

    // Final messages
}

class ServerMessage {
    public string Messagenumber {get; set;}
    public string Message {get; set;}
}

class ServerMessagePart {
    public string Messagenumber {get; set;}
    public string Message {get; set;}
    public int Partnumber {get; set;}
    public int Total {get; set;}
}

Upvotes: 0

Related Questions