Stevie Star
Stevie Star

Reputation: 2371

Javascript - How to transform array list into another array list of key values?

So I've been stumped on this for hours and I can't really figure out an elegant solution to solve this problem. Let's say I have an array like this:

[
   {
     question: "what is your name?",
     answer: "Ben",
     topic: "names"
   },
{
     question: "what is your name?",
     answer: "Ben",
     topic: "names"
   },
   {
     question: "What is dog's name?",
     answer: "Snugglets",
     topic: "names"
   },
   {
     question: "What is your brother's age?",
     answer: 55,
     topic: "ages"
   }
]

How can I turn this into an array that looks like this?

[
   {
     topic: "names",
     content: [...array of objects based on "names"...]
   },
{
     topic: "ages",
     content: [...array of objects based on "ages"...]
   }
]

I feel this is something that should be really easy, but for some reason my brain cannot seem to grasp the solution. What am I missing here?

UPDATE: Thanks for all the responses! I was not expecting to get this many methods of accomplishing this. I accepted one of the answers, as it was related to ES6 and I should have specified that I was looking to learn the ES6 way of doing this, if possible :)

Though, I must also say that the needs have also changed a bit where instead of the intended array, being like this:

[ { topic: "names", content: [...array of objects based on "names"...] }, { topic: "ages", content: [...array of objects based on "ages"...] } ]

The array needs to look like more like this:

[ { topic: "names", content: '<div> <h3>Question: What is your name?</h3> <p>Answer: Ben</p> </div> <div> <h3>Question: What is your dog's name?</h3> <p>Answer: Snugglets</p> </div>' }, { topic: "ages", content: content: '<div> <h3>Question: What is your age?</h3> <p>Answer: 50</p> </div> <div> <h3>Question: What is your brother's age?</h3> <p>Answer: 52</p> </div>' } ]

Normally I don't like to just ask for code given to me, but my Javascript foo seems to a bit weaker when it comes to transforming data algorithmically :( Any thoughts on how I could merge all of those array elements into a single string, containing HTML, that serves as the "content" key of each "topic"?

Also, would this be better served in another question on Stackoverflow?

Upvotes: 3

Views: 20682

Answers (5)

Redu
Redu

Reputation: 26161

Can be simply done as follows when ES6 Map object is utilized. I also removed the redundant names property from the items of the content array.

var data = [{question: "what is your name?",answer: "Ben",topic: "names"},{question: "what is your name?",answer: "Ben",topic: "names"},{question: "What is dog's name?",answer: "Snugglets",topic: "names"},{question: "What is your brother's age?",answer: 55,topic: "ages"}],
 mapData = data.reduce((p,c) => p.has(c.topic) ? p.set(c.topic,p.get(c.topic).concat({question:c.question,
                                                                                        answer:c.answer}))
                                               : p.set(c.topic,[{question:c.question,
                                                                   answer:c.answer}]),new Map()),
  result = [...mapData].map(e => ({topic:e[0],content:e[1]}));
console.log(result);

Upvotes: 1

HongShaoRou
HongShaoRou

Reputation: 9

There is a simple function for you.

var arr=[
   {
     question: "what is your name?",
     answer: "Ben",
     topic: "names"
   },
    {
     question: "what is your name?",
     answer: "Ben",
     topic: "names"
   },
   {
     question: "What is dog's name?",
     answer: "Snugglets",
     topic: "names"
   },
   {
     question: "What is your brother's age?",
     answer: 55,
     topic: "ages"
   }
]
function turnToOtherArray(arr){
    var result=[];
    for(var i=0,mamorize={};i<arr.length;i++){
        if(mamorize[arr[i].topic]){
            mamorize[arr[i].topic].push(arr[i].question)
        }else{
            mamorize[arr[i].topic]=[arr[i].question]
        }
    }
    for(var key in mamorize){
        result[result.length] = { "topic" : key , "content" : mamorize[key]}
    }
    return result;
}
console.log(turnToOtherArray(arr));

Upvotes: -1

KarlPurk
KarlPurk

Reputation: 275

A more functional approach is to use Array.reduce. This also removes the need for a temporary variable:

var output = data.reduce(function(prev, cur) {
  if (!prev[cur.topic]) prev[cur.topic] = [];
  prev[cur.topic].push(cur);
  return prev;
}, {});

See JS Bin for working example:
https://jsbin.com/yesucaquwa/1/edit?js,console

Upvotes: 1

Nina Scholz
Nina Scholz

Reputation: 386550

You could group it with a temporary object and iterate over with Array#forEach.

The forEach() method executes a provided function once per array element.

var data = [{ question: "what is your name?", answer: "Ben", topic: "names" }, { question: "what is your name?", answer: "Ben", topic: "names" }, { question: "What is dog's name?", answer: "Snugglets", topic: "names" }, { question: "What is your brother's age?", answer: 55, topic: "ages" }],
    group = [];

data.forEach(function (a) {
    if (!this[a.topic]) {
        this[a.topic] = { topic: a.topic, content: [] };
        group.push(this[a.topic]);
    }
    this[a.topic].content.push(a);
}, Object.create(null));

console.log(group);

Upvotes: 3

jcubic
jcubic

Reputation: 66478

Try this:

var input = [
   {
     question: "what is your name?",
     answer: "Ben",
     topic: "names"
   },
{
     question: "what is your name?",
     answer: "Ben",
     topic: "names"
   },
   {
     question: "What is dog's name?",
     answer: "Snugglets",
     topic: "names"
   },
   {
     question: "What is your brother's age?",
     answer: 55,
     topic: "ages"
   }
];
var tmp = input.reduce(function(topics, obj) {
  topics[obj.topic] = topics[obj.topic] || {topic: obj.topic, content: []};
  topics[obj.topic].content.push(obj);
  return topics;
}, {});
var output = Object.keys(tmp).map(function(key) {
  return tmp[key];
});
console.log(output);

Upvotes: 0

Related Questions