1

Assume

{
  "foo":[
    "baz"
  ],
  "bar": {
    "blarg": [
      "blippo"
    ],
    "blunder": {
      "bus": [
        {
         "bigly": [
          "bugnuts"
         ]
        }
      ]
    }
  },
  "blather": [
    {
      "bumpy": [
        "bugaloo"
      ]
    },
    {
      "blither": {
        "bother": [
          "bingo"
        ]
      }
    }
  ]
}

What would be the most efficient way (preferably using lodash) to convert such that all leaves that are arrays of one member now contain that member, and not the array? As in:

{
  "foo": "baz",
  "bar": {
    "blarg": "blippo",
    "blunder": {
      "bus": {
       "bigly": "bugnuts"
      }
    }
  },
  "blather": [
    {
      "bumpy": "bugaloo"
    },
    {
      "blither": {
        "bother": "bingo"
      }
    }
  ]
}

The object is much larger than the one I've presented here, and so has many possible paths.

I've tried first getting a list of paths, as in:

foo[0]
foo
bar.blarg[0]
bar.blarg
bar.blunder.bus[0]
bar.blunder.bus
bar.blunder
bar
blather[0].bumpy[0]
blather[0].bumpy
blather[0]
blather[1].blither.bother[0]
blather[1].blither.bother
blather[1].blither
blather[1]
blather

and attempting to do the mutation both depth and breadth first, but of course, the first mutation has the possibility of invalidating other paths.

I'm thinking it's a question of recursion, but the solution eludes me.

2 Answers 2

2

Here's a cloneDeepWith() approach that also covers collapsing arrays that contains a single object.

var result = _.cloneDeepWith(data, function customizer(value) {
  if(_.isArray(value) && _.size(value) === 1) {
    value = value[0];    
    return _.isObject(value)? 
      _.cloneDeepWith(value, customizer):
      value;
  }
});

console.log(result);

var data = {
  "foo":[
    "baz"
  ],
  "bar": {
    "blarg": [
      "blippo"
    ],
    "blunder": {
      "bus": [
        {
         "bigly": [
          "bugnuts"
         ]
        }
      ]
    }
  },
  "blather": [
    {
      "bumpy": [
        "bugaloo"
      ]
    },
    {
      "blither": {
        "bother": [
          "bingo"
        ]
      }
    }
  ]
};

var result = _.cloneDeepWith(data, function customizer(value) {
  if(_.isArray(value) && _.size(value) === 1) {
    return _.isObject(value[0])?
      _.cloneDeepWith(value[0], customizer):
      value[0];
  }
});

console.log(result);
body > div { min-height: 100%; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>

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

Comments

0

cloneDeepWith provides a way to customise an object as you clone it.

const {cloneDeepWith, isArray} = require('lodash')

const flatter = cloneDeepWith(data, value => {
  if ( isArray(value) && value.length === 1 ) return value[0]
})

A recursive forEach would allow you to mutate the existing object (or forIn/forOwn if required).

const {forEach} = require('lodash')

function process(obj){
  forEach(obj, (val, key)=> {
    if ( isArray(val) && val.length === 1 ) return obj[key] = val[0]
    if ( isObject(val) ) process(val)
  })
}

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.