2

I have a multi-dimensional array of objects that I need to transform into a different array. I'm convinced that _.map() is what I need. Not having used it before, I'm having trouble traversing the array to extract the correct values. Given this simplified example:

[
   {
      "08/25/2015":[
         {
            "source":"someSource0",
            "name":"someName0",
            "stuff":"-6.728479",
            "stuffValue":"14.862200",
            "amount":"-100.00"
         },
         {
            "notNeeded0":-100,
            "subtotal":"-100.00"
         }
      ]
   },
   {
      "08/26/2015":[
         {
            "source":"someSource1",
            "name":"someName1",
            "stuff":"-9.496676",
            "stuffValue":"10.530000",
            "amount":"-100.00"
         },
         {
            "notNeeded0":-100,
            "subtotal":"-100.00"
         }
      ]
   },
   {
      "08/27/2015":[
         {
            "source":"someSource2",
            "name":"someName2",
            "stuff":"-9.469697",
            "stuffValue":"10.560000",
            "amount":"-100.00"
         },
         {
            "notNeeded0":-100,
            "subtotal":"-100.00"
         }
      ]
   },
   {
      "08/28/2015":[
         {
            "source":"someSource3",
            "name":"someName3",
            "stuff":"-1.731841",
            "stuffValue":"10.570000",
            "amount":"-18.24"
         },
         {
            "source":"someSource4",
            "name":"someName4",
            "stuff":"-2.628939",
            "stuffValue":"31.100000",
            "amount":"-81.76"
         },
         {
            "notNeeded0":-100,
            "subtotal":"-100.00"
         }
      ]
   },
   {
      "notNeeded1":-400,
      "notNeeded2":"-400.00"
   }
]

I needed to transform it to something structured like this:

[
   {
      "date":"08/27/2015",
      "detail":[
         {
            "source":"someSource2",
            "name":"someName2",
            "stuff":"-9.469697",
            "stuffValue":"10.560000",
            "amount":"-100.00",
            "subtotal":"-100.00"
         }
      ]
   },
   {
      "date":"08/28/2015",
      "detail":[
         {
            "source":"someSource3",
            "name":"someName3",
            "stuff":"-1.731841",
            "stuffValue":"10.570000",
            "amount":"-18.24",
            "subtotal":"-100.00"
         },
         {
            "source":"someSource4",
            "name":"someName4",
            "stuff":"-2.628939",
            "stuffValue":"31.100000",
            "amount":"-81.76",
            "subtotal":"-100.00"
         }
      ]
   }
]

Please feel free to ask questions if needed. Thanks for your assistance.

EDIT: This code is not for web-based use. So any reference to web and browser compatibility is not necessary. And, I use the underscore library to simplify tasks like looping that can get pretty ugly using pure JS. Hope that clarifies the intent.

4
  • you can use javascript's own array.filter(keep_only_dates).map(transform) - unless you need to support IE8 Commented Feb 6, 2016 at 21:44
  • .map is pretty straightforward: The callback is invoked for every element in the array and the return value is added to the new array. The documentation even has examples: underscorejs.org/#map . Commented Feb 6, 2016 at 22:10
  • @FelixKling i've read the doc first. it's simplified one-level example didn't help me much which is why I posted this question here. It would probably have helped if I had mentioned more about what I've already done. I'll remember that. Thx. Commented Feb 7, 2016 at 15:37
  • Oftentimes, I fail to decompose the problem into manageable bits. this example is no different. one solution a colleague proposed is to break out the details (and a totals node not in the OP) and roll them into the final object as evidenced in this updated gist. gist.github.com/mjstelly/b7cbfdfc3efda5a90636 Commented Feb 9, 2016 at 12:47

2 Answers 2

1

using built-in .filter, .map and .splice:

var modified = original.filter(function only_nested_arrays(obj) {
  var keys = Object.keys(obj)
  return keys.length === 1 && Array.isArray(obj[keys[0]])
}).map(function transform(obj) {
  var date = Object.keys(obj)[0],
      inner_array = obj[date],
      subtotal = inner_array.splice(-1)[0].subtotal
  inner_array.forEach(function(obj_inner) {
    obj_inner.subtotal = subtotal
  })
  return {date: date, detail: inner_array}
})

console.log(JSON.stringify(modified, null, 2))

But watch out for browser compatibility (see the links for a polyfill to make it work on IE8 or lower). Runnable code that alerts the result if you want to test:

"use strict"
var original = [
  {
    "08/25/2015":[
      {
        "source":"someSource0",
        "name":"someName0",
        "stuff":"-6.728479",
        "stuffValue":"14.862200",
        "amount":"-100.00"
      },
      {
        "notNeeded0":-100,
        "subtotal":"-100.00"
      }
    ]
  },
  {
    "08/26/2015":[
      {
        "source":"someSource1",
        "name":"someName1",
        "stuff":"-9.496676",
        "stuffValue":"10.530000",
        "amount":"-100.00"
      },
      {
        "notNeeded0":-100,
        "subtotal":"-100.00"
      }
    ]
  },
  {
    "08/27/2015":[
      {
        "source":"someSource2",
        "name":"someName2",
        "stuff":"-9.469697",
        "stuffValue":"10.560000",
        "amount":"-100.00"
      },
      {
        "notNeeded0":-100,
        "subtotal":"-100.00"
      }
    ]
  },
  {
    "08/28/2015":[
      {
        "source":"someSource3",
        "name":"someName3",
        "stuff":"-1.731841",
        "stuffValue":"10.570000",
        "amount":"-18.24"
      },
      {
        "source":"someSource4",
        "name":"someName4",
        "stuff":"-2.628939",
        "stuffValue":"31.100000",
        "amount":"-81.76"
      },
      {
        "notNeeded0":-100,
        "subtotal":"-100.00"
      }
    ]
  },
  {
    "notNeeded1":-400,
    "notNeeded2":"-400.00"
  }
]

var modified = original.filter(function only_nested_arrays(obj) {
  var keys = Object.keys(obj)
  return keys.length === 1 && Array.isArray(obj[keys[0]])
}).map(function transform(obj) {
  var date = Object.keys(obj)[0],
      inner_array = obj[date],
      subtotal = inner_array.splice(-1)[0].subtotal
  inner_array.forEach(function(obj_inner) {
    obj_inner.subtotal = subtotal
  })
  return {date: date, detail: inner_array}
})

alert(JSON.stringify(modified, null, 2))

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

2 Comments

I like the JSON stringify formatting. I'd forgotten it could do that. It makes it much easier to read.
caveat: I didn't actually test this on my code. As often happens in business, the development changed directions and we no longer need this algorithm. I wanted to give credit for the assistance. That has intrinsic value to our community. Thanks, @aprillion
0

Using _.filter, _.map & _.chain here is what it would look like.

var transformed = _.chain(data).filter(function (obj) {
    var keys = _.keys(obj);
    // only allow where obj has only one key & the value is an array
    // consider replacing this with a check to see if the key is a date which should be more appropriate
    return keys.length === 1 && _.isArray(obj[keys[0]]);
}).map(function (obj) {
    var date     = _.keys(obj)[0];
    var details  = obj[date];
    var subtotal = details.splice(-1)[0].subtotal; // get the last obj from array destructively
    details      = _.map(details, function (detail) {
        detail.subtotal = subtotal; // append the subtotal to all the remaining detail objects
        return detail
    });
    return {
        date   : date,
        details: details
    }
}).value();

I'd suggest that you use the native functions as shown in Aprillion's answer so that you can avoid the whole chain() & then value() usages for getting the actual value from the wrapped object. You can use underscore when the requirements are a bit more complicated.

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.