3

I have some example data that shows some data related to docs (docs.id) and the people which it refers to (details.id) :

const docs = [
        {
          id: "89",
          state: "accepted",
          details: [
            {
              id: 20656,
              type: "Claimant",
              name: "First Name Last Name",
              first_name: "First Name",
              last_name: "Last Name",
              type_label: "claimant"
            }
          ]
        },
        {
          id: "45",
          state: "accepted",
          details: [
            {
              id: 20656,
              type: "Claimant",
              name: "First Name Last Name",
              first_name: "First Name",
              last_name: "Last Name",
              type_label: "claimant"
            },
            {
              id: 20657,
              type: "Fellow",
              name: "Fellow First Name Fellow Last Name",
              first_name: "Fellow First Name",
              last_name: "Fellow Last Name",
              type_label: "fellow"
            }
          ]
        },
        {
          id: "47",
          state: "rejected",
          details: [
            {
              id: 20656,
              type: "Claimant",
              name: "First Name Last Name",
              first_name: "First Name",
              last_name: "Last Name",
              type_label: "claimant"
            }
          ]
        }
      ]
      
    const groups = docs.reduce((groups, item) => {
    const group = groups[item.details] || [];
    group.push(item);
    groups[item.details] = group;
    return groups;
  }, {});

  console.log("groups: ", groups);

I'm trying to manipulate this array so that I could group per person (details.id) all her related docs (docs.id) so that I can later on use the results in react app but it's not working like that.

EDIT (adding expected result):

  const new = [
      {
        id: 20656,
        type: "Claimant",
        name: "First Name Last Name",
        docs: [89,45,47]
      },
      {
        id: 20656,
        type: "Fellow",
        name: "Fellow First Name Fellow Last Name",
        docs: [47]
      }
    ]
3
  • groups[item.details] = ... - item.details is itself an object, so it makes little sense to try and use that as key. Commented Nov 21, 2022 at 12:17
  • 3
    What's the expected result of the above example? Commented Nov 21, 2022 at 12:19
  • You need to loop thorugh item.details and add id as the key to groups accumulator Commented Nov 21, 2022 at 12:20

3 Answers 3

1
  • Using Array#reduce, iterate over docs while updating a Map where the key is the detail-id and the value is the accumulated details with the docs array
  • In each iteration, iterate over the current details using Array#forEach:
    • Using Map#get, get the accumulated object for the current detail-id, otherwise use an initial one
    • Using Array#push, add the doc-id to its docs array
    • Using Map#set, update the map with the new value
  • Using Map#values, return the list of grouped items

const docs = [
  {
    id: "89",
    state: "accepted",
    details: [
      { id: 20656, type: "Claimant", name: "First Name Last Name", first_name: "First Name", last_name: "Last Name", type_label: "claimant" }
    ]
  },
  {
    id: "45",
    state: "accepted",
    details: [
      { id: 20656, type: "Claimant", name: "First Name Last Name", first_name: "First Name", last_name: "Last Name", type_label: "claimant" },
      { id: 20657, type: "Fellow", name: "Fellow First Name Fellow Last Name", first_name: "Fellow First Name", last_name: "Fellow Last Name", type_label: "fellow" }
    ]
  },
  {
    id: "47",
    state: "rejected",
    details: [
      { id: 20656, type: "Claimant", name: "First Name Last Name", first_name: "First Name", last_name: "Last Name", type_label: "claimant" }
    ]
  }
];
      
const groups = [...
  docs.reduce((detailMap, { id: docId, state, details = [] }) => {
    details.forEach(({ id, type, name }) => {
      const detail = detailMap.get(id) ?? { id, type, name, docs: [] };
      detail.docs.push(docId);
      detailMap.set(id, detail);
    });
    return detailMap;
  }, new Map)
  .values()
];

console.log("groups: ", groups);

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

3 Comments

Although other solutions also work, your explanation was clearer so I voted it as the right answer. I was able to enrich it a bit. Later I found out that I might get into trouble with having the repeated docs when a doc belongs to more than one person. Ideally, for example for doc (id: "45"), it should be separated as it belongs to 2 - short example: ` { "id": ["20657", "20656"], "docs": [ "45" ] }`. Any idea on how to accomplish it?
@Whoopcg this is a major change, I suggest writing another question with clear input and output example.
thank you for your help according to my request - which is still a correct answer and will ask another question.
1

Using reduce() is a right choice,but we need to iterate the details array

let result = docs.reduce((a,v)=>{
  let docId = v.id
  v.details.forEach(d => {
     let pId = d.id
     let obj = a.find(i => i.personId === pId)
     if(obj){
       obj.docId.push(docId)
     }else{
       a.push({'personId':pId,'docId':[docId]})
     }
   })
   return a
},[])  

const docs = [
        {
          id: "89",
          state: "accepted",
          details: [
            {
              id: 20656,
              type: "Claimant",
              name: "First Name Last Name",
              first_name: "First Name",
              last_name: "Last Name",
              type_label: "claimant"
            }
          ]
        },
        {
          id: "45",
          state: "accepted",
          details: [
            {
              id: 20656,
              type: "Claimant",
              name: "First Name Last Name",
              first_name: "First Name",
              last_name: "Last Name",
              type_label: "claimant"
            },
            {
              id: 20657,
              type: "Fellow",
              name: "Fellow First Name Fellow Last Name",
              first_name: "Fellow First Name",
              last_name: "Fellow Last Name",
              type_label: "fellow"
            }
          ]
        },
        {
          id: "47",
          state: "rejected",
          details: [
            {
              id: 20656,
              type: "Claimant",
              name: "First Name Last Name",
              first_name: "First Name",
              last_name: "Last Name",
              type_label: "claimant"
            }
          ]
        }
      ]
      
let result = docs.reduce((a,v)=>{
  let docId = v.id
  v.details.forEach(d => {
     let pId = d.id
     let obj = a.find(i => i.personId === pId)
     if(obj){
       obj.docId.push(docId)
     }else{
       a.push({'personId':pId,'docId':[docId]})
     }
   })
   return a
},[])   
      
console.log(result)

1 Comment

this will work, also "find" operation is costly here, can we memoise it?
1

Logic.

  1. Generate list of details
  2. Group it against the id.

Array.reduce implementation

const docs = [
  {
    id: "89",
    state: "accepted",
    details: [
      { id: 20656, type: "Claimant", name: "First Name Last Name", first_name: "First Name", last_name: "Last Name", type_label: "claimant" },
    ],
  },
  {
    id: "45",
    state: "accepted",
    details: [
      { id: 20656, type: "Claimant", name: "First Name Last Name", first_name: "First Name", last_name: "Last Name", type_label: "claimant" },
      { id: 20657, type: "Fellow", name: "Fellow First Name Fellow Last Name",               first_name: "Fellow First Name",               last_name: "Fellow Last Name", type_label: "fellow" },
    ],
  },
  {
    id: "47",
    state: "rejected",
    details: [
      { id: 20656, type: "Claimant", name: "First Name Last Name", first_name: "First Name", last_name: "Last Name", type_label: "claimant" },
    ],
  },
];

const groups = docs
  .map((node) =>  node.details.map((item) => ({
    id: item.id,
    type: item.type,
    name: item.name,
    parentId: node.id,
  })))
  .flat()
  .reduce((acc, curr) => {
    acc[curr.id] = acc[curr.id] ?? curr;
    if (acc[curr.id].docs) {
      acc[curr.id].docs.push(curr.parentId)
    } else {
      acc[curr.id].docs = [curr.parentId]
    }
    return acc;
  }, {})

console.log(Object.values(groups));

2 Comments

isn't it returning extra data in docs? I see for example "89", "20656", "20656"
@Whoopcg Oops that was a typo, corrected that

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.