1

I have an array of objects similar to

const array = [
{
    documentId: "document1",
    writers: [
        {
            name: "writer1", 
            date: "2017-12-11"
        },
        {
            name: "writer2", 
            date: "2017-12-11"
        }
    ]
},
{
    documentId: "document2",
    writers: [
        {
            name: "writer1", 
            date: "2017-12-11"
        },
        {
            name: "writer3", 
            date: "2017-12-11"
        }
    ]
},
{
    documentId: "document3",
    writers: [
        {
            name: "writer3", 
            date: "2017-12-11"
        },
        {
            name: "writer4", 
            date: "2017-12-11"
        }
    ]
},

I'm trying to extract all the unique writer's names and match them to all the documents they have written so that the final array looks something like this:

const finalArray = [
    {name: "writter1", documents: ["document1", "document2"]},
    {name: "writter2", documents: ["document1"]},
    {name: "writter3", documents: ["document2", "document3"]},
    {name: "writter4", documents: ["document3"]}
]
1
  • Welcome to SO. Please include attempted solutions, why they didn't work, and the expected results. That would really help us to figure out the issue with your code. Thanks! Commented Dec 11, 2017 at 17:04

3 Answers 3

2

You can use Map to keep writers as keys then call reduce on your array to map documents to writers, then call values() on the Map object:

const ret = Array.from(array.reduce((acc, val) => {
  val.writers.forEach(({ name }) => {
    let w = acc.get(name) || { name: name, documents: [] };
    w.documents = w.documents.concat(val.documentId);
    acc.set(name, w);
  });
  return acc;
}, new Map()).values());

console.log(ret);
Sign up to request clarification or add additional context in comments.

Comments

1

Maybe there will be a more elegant map-reduce solution. Anyway, this will works even if it's ugly:

const array = //your data
var output = []
for (let i = 0; i < array.length; i++)
    for (let k = 0; k < array[i].writers.length; k++) {
        let t = output.find(function(a){return array[i].writers[k].name === a.name})
        if (t)
            t.documents.push(array[i].documentId)
        else
            output.push({name: array[i].writers[k].name, documents: [array[i].documentId]})
  }
console.log(output)

https://jsfiddle.net/50oph5g4/1/

Comments

1

You could take a Map which takes all names and the associated documents. If no map is avaliable for a name, then create a new entry.

A then end, get all objects of the map.

var array = [{ documentId: "document1", writers: [{ name: "writer1", date: "2017-12-11" }, { name: "writer2", date: "2017-12-11" }] }, { documentId: "document2", writers: [{ name: "writer1", date: "2017-12-11" }, { name: "writer3", date: "2017-12-11" }] }, { documentId: "document3", writers: [{ name: "writer3", date: "2017-12-11" }, { name: "writer4", date: "2017-12-11" }] }],
    map = new Map,
    result;

array.forEach(function (o) {
    o.writers.forEach(function ({ name }) {
        map.has(name) || map.set(name, { name, documents: [] });
        map.get(name).documents.push(o.documentId);
    });
});

result = Array.from(map.values());

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

Comments

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.