6
\$\begingroup\$

I'm reducing a large array of objects to a simple object with 8 properties. I'm currently using Array.prototype.reduce to loop through the array of objects to create a new object. In order to assign a correct key to the value in the array I'm using a switch statement. My code currently works, but I'm looking to refine it. I have no control over the API and how it returns its response.

The array of objects that are returned look like this:

[
  {
    field_id:89107336,
    values : [{ value:"Star Warz"}]
  },
  {
    field_id:89107337,
    values : [{ date:"December 24, 2015"}]
  },
  {
    field_id:89107415,
    values : [
               {
                 value: { item_id:1234123}
               },
               {
                 value: { item_id:4746576}
               }
  },
  {
    field_id:89107340,
    values : [{ value:"5.00"}]
  },
  {
    field_id:89107344,
    values : [{ value:"This is description"}]
  },
  {
    field_id:89107348,
    values : [
               { 
                 value:{file_id:234234}
               }
             ]
  }
]   

The object values come back random at times, so the only way for me to tell the difference between the title, the date, description, etc. Is for me to run the response through a switch statement based upon field_id. So I created an object like so:

var eventFieldIds = {
        title: 89107336,
        date: 89107337,
        attendees: 89107415,
        price: 89107340,
        desc: 89107344,
        img: 89107348
    };

Then I use a switch statement to reduce the array objects like this:

var newEvent = arrayObjects.reduce(function(p, c) {
    switch (c.field_id) {
        case eventFieldIds.title:
            {
                p.name = c.values[0].value;
                break;
            }
        case eventFieldIds.date:
            {
                p.date = new Date(c.values[0].start_date);
                break;
            }
        case eventFieldIds.attendees:
            {
                //this is an array of values
                var tempArray = c.values.map(function(v) {
                    return value.value.item_id;
                });
                p.attendees = JSON.stringify(tempArray);
                break;
            }
        case eventFieldIds.price:
            {
                p.price = c.values[0].value;
                break;
            }
        case eventFieldIds.desc:
            {
                p.description = c.values[0].value;
                break;
            }
        case eventFieldIds.img:
            {
                p.img = {
                    file_id: c.values[0].value.file_id
                };
                break;
            }
    }

    return p;
}, {});

How can I refine this? Can I cut out the switch statement? Is there a better way to do this?

\$\endgroup\$
5
\$\begingroup\$

I'd flip the field-id-to-name lookup around, and give it some more detail; e.g.

function defaultConverter(values) {
  return values[0].value;
} 

var mappings = {
  "89107336": { name: "title", converter: defaultConverter },
  "89107340": { name: "price", converter: defaultConverter },
  "89107344": { name: "desc", converter: defaultConverter },
  "89107337": {
    name: "date",
    converter: function (values) {
      return new Date(values[0].start_date);
    }
  },
  "89107348": {
    name: "img",
    converter: function (values) {
      return values[0].value.file_id;
    }
  },
  "89107415": {
    name: "attendees",
    converter: function (values) {
      var items = values.map(function (v) { return v.value.item_id });
      return JSON.stringify(items);
    }
  }
};

This way you declare how each piece of data should be treated, and you can easily extend it.

Your reduce can then become this:

var output = data.reduce(function (output, obj) {
  var mapper = mappings[obj.field_id];
  if(map) {
    output[map.name] = map.converter(obj.values)
  }
  return output;
}, {});

You can make the mapping declaration a little less repetitive like so:

function Mapping(name, converter) {
  this.name = name;
  this.converter = converter || function (values) { return values[0].value };
}

var mappings = {
  "89107336": new Mapping("title"),
  "89107340": new Mapping("price"),
  "89107344": new Mapping("desc"),
  "89107337": new Mapping("date", function (values) {
    return new Date(values[0].start_date);
  }),
  "89107348": new Mapping("img", function (values) {
    return values[0].value.file_id;
  }),
  "89107415": new Mapping("attendees", function (values) {
    var items = values.map(function (v) { return v.value.item_id });
    return JSON.stringify(items);
  })
};

and still use the same reduce function.

\$\endgroup\$
  • \$\begingroup\$ This looks great! Flambino, I've read alot of books recently to try to become better at Javascript like, "Effective Javascript", "Javascript Patterns", "Javascript the Good Parts". I can understand everything you did, but yet I haven't become fluent enough indesign. What would you recommend for me to study or do now? \$\endgroup\$ – inspired Dec 21 '15 at 20:19
  • \$\begingroup\$ @inspired That's a tough one. I can't think of any books; I'd recommend just practicing. Find some code exercises, do some hobby projects, etc.. Anything to hone your skills, really. But also: Look at other people's code. I learned a lot this way. You can also review things on this site! Design-wise, I personally like to go fairly top-down. For each part of a problem I think "I want to be able to just write [some imaginary, ideal piece of code], so how do I get to that point?". Along the way, look for ways to encapsulate things, avoid repetition, add flexibility, decouple things, etc.. \$\endgroup\$ – Flambino Dec 21 '15 at 21:01
  • \$\begingroup\$ Flambino, Thanks alot for your help! I've read alot JS books, in the interest of become as proficient as possible. I love the language \$\endgroup\$ – inspired Dec 22 '15 at 0:41
  • \$\begingroup\$ Flambino, would it be beneficial to add the converter method to the Mappings.prototype, so that it won't be attached to each instance? Then I could attachdefaultConverter, dateConverter,attendeeConverter methods? Would that be useful? \$\endgroup\$ – inspired Dec 22 '15 at 18:28
  • \$\begingroup\$ @inspired I wouldn't bother. A mapping instance should be generic. Giving it a bunch of specialized prototype methods would defeat that. It should not know or care about attendees or dates. A mapping instance would just end up with a lot of clutter, and you'd have to rework the prototype to add more conversions. In the code above, writing new Mapping(a, b) is really just another way of writing { name: a, converter: b } with just a touch more formality. But if you find something that'd be useful for all mappings, then yes: Prototype method. \$\endgroup\$ – Flambino Dec 22 '15 at 19:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.