1

I have a json where I need to reduce the items when they are of a type (isSender).
I need to reduce them when they are occurring consecutively.
The data can change.

The data looks like this:

 "messages": [
    { "content": ["Foo"], "isSender": true },
    { "content": ["Bar"], "isSender": true },
    { "content": ["Lorem"], "isSender": true },
    { "content": ["Ipsum"], "isSender": true },
    { "content": ["Dolor"], "isSender": false },
    { "content": ["Sit Amet"], "isSender": false },
    { "content": ["No"], "isSender": true }
  ]

I need the content to be an array of messages when the consecutive isSender key is the same. The output should look like this:

  "messages": [
    {
      "content": ["Foo", "Bar", "Lorem", "Ipsum"],
      "isSender": true
    },
    { "content": ["Dolor", "Sit amet"], "isSender": false },
    { "content": ["No"], "isSender": true }
  ]

So far I have tried looping through the message array and checking if next messages have the same isSender key. However this does not work for more than 2 messages.

let deleteIndex = [];
 for(let i = 0  ; i < messages.length - 1; i++) {
        const currMs = messages[i];
        const nextMs = messages[i + 1];
        if (nextMs.isSender == currMs.isSender) {
            currMs.content.push(nextMs)
            deleteIndex.push(i + 1) // saving index to be deleted once for loop is done
        }
    }

Can someone give me a clue on how to work around this?

Thanks in advance.

0stone0
  • 34,288
  • 4
  • 39
  • 64
  • What does this have to do with JSON? I assume you have no problem with JSON.parse or JSON.serialize. The JSON part of the problem is irrelevant/simple/solved, right? You are interested in transforming a JavaScript object from the input format to the output format efficiently using economical syntax, right? You are basically asking for a GroupBy kind of grouping of objects that respects _runs_. You only want to group consecutive elements. Kind of like how run length encoding works. Am I right? – Wyck Jan 19 '23 at 14:13
  • @Wyck many, many beginners don't understand what JSON is. – Robo Robok Jan 19 '23 at 14:28

2 Answers2

0

Use reduce(), on each iteration, we check if the last element in the result array has the same isSender value as the current object we're dealing with.

if (p.length && p.at(-1).isSender === c.isSender) {

So, if we already have something in the final array (p.length) AND the last isSender (p.at(-1).isSender) has the same value as the current isSender (c.isSender), append to that content list

Otherwise, add the entry, which is just the current object itself (return [ ...p, c ])

const data =  [
    { "content": ["Foo"], "isSender": true },
    { "content": ["Bar"], "isSender": true },
    { "content": ["Lorem"], "isSender": true },
    { "content": ["Ipsum"], "isSender": true },
    { "content": ["Dolor"], "isSender": false },
    { "content": ["Sit Amet"], "isSender": false },
    { "content": ["No"], "isSender": true }
  ];
  
const res = data.reduce((p, c) => {
    if (p.length && p.at(-1).isSender === c.isSender) {
        p.at(-1).content.push(...c.content);
        return p;
    }
    return [ ...p, c ];
}, [])

console.log(res);

Should you not like the push way, we can use Object.assign to alter the index itself:

let i = p.length - 1;
return Object.assign([...p], { [i]: { ...p[i], content: [ ...p[i].content, ...c.content ]}});

const data =  [
    { "content": ["Foo"], "isSender": true },
    { "content": ["Bar"], "isSender": true },
    { "content": ["Lorem"], "isSender": true },
    { "content": ["Ipsum"], "isSender": true },
    { "content": ["Dolor"], "isSender": false },
    { "content": ["Sit Amet"], "isSender": false },
    { "content": ["No"], "isSender": true }
  ];
  
const res = data.reduce((p, c) => {
    if (p.length && p.at(-1).isSender === c.isSender) {
        let i = p.length - 1;
        return Object.assign([...p], { [i]: { ...p[i], content: [ ...p[i].content, ...c.content ]}});
    }
    return [ ...p, c ];
}, [])

console.log(res);

Javascript spread on array index to 'push' new item to nested array


[
  {
    "content": ["Foo", "Bar", "Lorem", "Ipsum"],
    "isSender": true
  },
  {
    "content": ["Dolor", "Sit Amet"],
    "isSender": false
  },
  {
    "content": ["No"],
    "isSender": true
  }
]
0stone0
  • 34,288
  • 4
  • 39
  • 64
  • 1
    I'm not the downvoter, but you do have a typo "were we check". It could also be that it mutates the original data (the `content` array of some of the original elements gets modified). Just random guesses, though. I actually think this answer is fine. – Wyck Jan 19 '23 at 14:22
  • I'm not a downvoter neither, but you can do better than those ugly `.push`-es. No side effects in `.reduce()`, please. – Robo Robok Jan 19 '23 at 14:29
  • I choose for push for simplicity. I will change it to a return with spreading the new item in a sec – 0stone0 Jan 19 '23 at 14:39
  • @RoboRobok Rather than making the solution horribly inefficient, just don't use `reduce` where a normal loop fits better – Bergi Jan 20 '23 at 01:42
  • @Bergi the thing is that it doesn't matter when it comes to performance. I mean OP won't run it over millions of entries I suppose. This is where readability is more important. I personally almost never use loops to produce a value. – Robo Robok Jan 20 '23 at 12:38
-2

you can try this

var res = {
  messages: [
    { content: [], isSender: true },
    { content: [], isSender: false },
    { content: [], isSender: false }
  ]
};

messages.messages.forEach((item) => {
  if (item.content[0] == "No")  {
res.messages[2].content = ["No"];
res.messages[2].isSender = item.isSender;
  }
  else {
    if (item.isSender)
       res.messages[0].content.push(...item.content);
    else res.messages[1].content.push(...item.content);
  }
});

console.log(JSON.stringify(res, null, 4));

json output

{
  "messages": [
    {
      "content": [
        "Foo",
        "Bar",
        "Lorem",
        "Ipsum"
      ],
      "isSender": true
    },
    {
      "content": [
        "Dolor",
        "Sit Amet"
      ],
      "isSender": false
    },
    {
      "content": [
        "No"
      ],
      "isSender": true
    }
  ]
}
Serge
  • 40,935
  • 4
  • 18
  • 45