0

I am trying to group related JavaScript objects in an array by key.

But, I'm at stuck at a point.

I have a JavaScript object array something like this:

[
    {
        "text": "hello world",
        "startPos": 0,
        "endPos": 12,
        "value": "hello world",
        "entity": "Greeting"
    },
    {
        "text": "hello world",
        "startPos": 0,
        "endPos": 6,
        "value": "hello",
        "entity": "Greeting"
    }
]

I made the following code, but I was stuck.

a = [
  {
    "text": "hello world",
    "startPos": 0,
    "endPos": 12,
    "value": "hello world",
    "entity": "Greeting"
  },
  {
    "text": "hello world",
    "startPos": 0,
    "endPos": 6,
    "value": "hello",
    "entity": "Greeting"
  }
]

let result = a.reduce((acc, d) => {
  const found = acc.find(a => a.text == d.text);
  const entitiesArray = {
    startPos: d.startPos,
    endPos: d.endPos,
    entity: d.entity
  };
  if (found) {
    found.entities.push(entitiesArray);
  } else {
    acc.push({});
  }
  return acc;
}, []);

console.log(JSON.stringify(result, undefined, 4));

The JavaScript object are being repeated based on the "text" key. I want to group the above array into one object based on the "text" key.

Something like this:

[
    {
        "text": "hello world",
        "entities": [
                {
                    "startPos": 0,
                    "endPos": 12,
                    "value": "hello world",
                    "entity": "Greeting"
                },
                {
                    "startPos": 0,
                    "endPos": 6,
                    "value": "hello",
                    "entity": "Greeting"
                }
        ]
    }
]
5
  • Do you expect "text": "hello world" in second object? Or is it copy paste issue Commented Feb 26, 2018 at 9:45
  • Why is the first entity has no text and the second entity has no entity? Commented Feb 26, 2018 at 9:45
  • No, I am getting two "distinct" objects whose text key is same, I want to group them. Commented Feb 26, 2018 at 9:46
  • But the objects you have on your entities (desired output) are different from what you started Commented Feb 26, 2018 at 9:46
  • @Eddie updated the question. Commented Feb 26, 2018 at 9:47

3 Answers 3

2

You missed to add the object with text into the array when found is false. So in the next iterations it can't be able to find object comparing their texts

Also if your IDE and Engines support new ES proposal, you can use Rest/Spread Properties

const a = [
  {
    "text": "hello world",
    "startPos": 0,
    "endPos": 12,
    "value": "hello world",
    "entity": "Greeting"
  },
  {
    "text": "hello world",
    "startPos": 0,
    "endPos": 6,
    "value": "hello",
    "entity": "Greeting"
  }
]

let result = a.reduce((acc, d) => {
  const found = acc.find(a => a.text == d.text);
  
  const { text, ...entitiesArray } = { ...d }; // <- Rest/Spread properties
  
  found ? found.entities.push(entitiesArray) 
        : acc.push({ text: d.text, entities: [entitiesArray]})

  return acc;
}, []);

console.log(result);

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

4 Comments

const entitiesArray = Object.assign({}, d); its copying the entire object, which has text key also, I only want the selected properties, as described in the question.
Your input data does not match OPs input data.
const { text, ...entitiesArray } = { ...d }; // <- Rest/Spread properties what does this line do?
I have added link into it where you can read more. In concise, it gets the property text and assigns it to the text variable in the left side, rest properties are just copied into the entitiesArray
2

You can also do a shorter version using reduce and Object.values

let a = [{
    "text": "hello world",
    "startPos": 0,
    "endPos": 12,
    "value": "hello world",
    "entity": "Greeting"
  },
  {
    "text": "hello world",
    "startPos": 0,
    "endPos": 6,
    "value": "hello",
    "entity": "Greeting"
  }
];

let result = Object.values(a.reduce((c, v) => {
  c[v.text] = c[v.text] || {text: v.text,entities: []};
  c[v.text].entities.push(v);
  return c;
}, {}));

console.log(result);


Without the text property

let a = [{
    "text": "hello world",
    "startPos": 0,
    "endPos": 12,
    "value": "hello world",
    "entity": "Greeting"
  },
  {
    "text": "hello world",
    "startPos": 0,
    "endPos": 6,
    "value": "hello",
    "entity": "Greeting"
  }
];

let result = Object.values(a.reduce((c, v) => {
  let {text: _,...o} = v;
  c[v.text] = c[v.text] || {text: v.text,entities: []};
  c[v.text].entities.push(o);
  return c;
}, {}));

console.log(result);

Comments

0

You could add a new object with text and an array for the entitites.

This solution takes a destructed object and builds new objects with the keys.

var a = [{ text: "hello world", startPos: 0, endPos: 12, value: "hello world", entity: "Greeting" }, { text: "hello world", startPos: 0, endPos: 6, value: "hello", entity: "Greeting" }],
    result = a.reduce((acc, { text, startPos, endPos, entity, value }) => {
        var found = acc.find(a => a.text === text),
            subEntity = { startPos, endPos, entity, value };

        if (found) {
            found.entities.push(subEntity);
        } else {
            acc.push({ text, entities: [subEntity] });
        }
        return acc;
    }, []);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

With rest parameters ... for destructuring properties and BABEL.

var a = [{ text: "hello world", startPos: 0, endPos: 12, value: "hello world", entity: "Greeting" }, { text: "hello world", startPos: 0, endPos: 6, value: "hello", entity: "Greeting" }],
    result = a.reduce((acc, { text, ...subEntity }) => {
        var found = acc.find(a => a.text === text);

        if (found) {
            found.entities.push(subEntity);
        } else {
            acc.push({ text, entities: [subEntity] });
        }
        return acc;
    }, []);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

4 Comments

So I have to mention all the keys of the object here a.reduce((acc, { text, startPos, endPos, entity, value }) => { alongwith the accumulator?
actually yes, because the rest syntax does not work in all browsers.
The rest parameters are supported in browsers which support ES6 no?
it is not supported for objects. in function, it is supported.

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.