Svetlana Ivanova
Svetlana Ivanova

Reputation: 155

Unzip object which contain array into array of object using ramda

I'm noob in functional programming (and ramda). And i have no idea how to make from this:

[{  
      "name":"SBRF IANUA EFS CC Integration",
      "expand":false,
      "check":"N",
      "jsonDiffPath":"/repo/Dynamics/Business Service/SBRF IANUA EFS CC Integration",
      "changeCount":1,
      "items":[  
         {  
            "name":"CRP-37920",
            "expand":false,
            "check":"N",
            "jsonDiffPath":null,
            "changeCount":1,
            "items":[],
            "op":"MODIFY",
            "oldSnapshot":"723012",
            "newSnapshot":"948053",
            "myChange":false
         }
      ],
"id":"F5ZGK4DPF5CHS3TBNVUWG4ZPIJ2XG2LOMVZXGICTMVZHM2LDMUXVGQSSIYQESQKOKVASARKGKMQEGQZAJFXHIZLHOJQXI2LPNY======",
      "objectId":"30263",
      "group":"repo",
      "category":"Dynamics",
      "type":"Business Service"
   }
]

something like this:

[  
   {  
      "name":"[CRP-37920] SBRF IANUA EFS CC Integration",
      "expand":false,
      "check":"N",
      "jsonDiffPath":"/repo/Dynamics/Business Service/SBRF IANUA EFS CC Integration",
      "changeCount":1,
      "op":"MODIFY",
      "oldSnapshot":"723012",
      "newSnapshot":"948053",
      "items":[],
  "id":"F5ZGK4DPF5CHS3TBNVUWG4ZPIJ2XG2LOMVZXGICTMVZHM2LDMUXVGQSSIYQESQKOKVASARKGKMQEGQZAJFXHIZLHOJQXI2LPNY======",
      "objectId":"30263",
      "group":"repo",
      "category":"Dynamics",
      "type":"Business Service"
   }
]

Each elements of "items" array should be merged with it parent (Object with inner array => into array of objects )

Thanks in advance)

Upvotes: 0

Views: 488

Answers (3)

Chris Phillips
Chris Phillips

Reputation: 673

I'm new to Ramda, but thought I'd give it a go. My solution does include the custom name merge rule from your example.

let concatName = (key, parent, children) => key === 'name' ? `[${children}] ${parent}` : children;

const mergeItems = R.map((item ) => {
    return R.mergeWithKey(concatName, item, R.mergeAll(R.prop('items',item)))}
)

Since it wasn't clear how "deep" nested items might be, nor what the merging rules should be in that case, I didn't make it go deeper.

Upvotes: 0

Scott Sauyet
Scott Sauyet

Reputation: 50797

One possibility, if I understand your requirements correctly would be something like this:

const alterOne = chain(
  children => parent => map(merge(__, dissoc('items', parent)), children),
  prop('items')
)

const alterAll = pipe(map(alterOne), flatten)

There's probably some good way to make the first function passed to chain in alterOne point-free, but this seems pretty readable to me already.

You can see this in the Ramda REPL.

Upvotes: 1

kyle
kyle

Reputation: 2638

// array of parents
const p = [
  {
    name: 'SBRF IANUA EFS CC Integration',
    expand: false,
    check: 'N',
    jsonDiffPath: '/repo/Dynamics/Business Service/SBRF IANUA EFS CC Integration',
    changeCount: 1,
    items: [
      {
        name: 'CRP-37920',
        expand: false,
        check: 'N',
        jsonDiffPath: null,
        changeCount: 1,
        items: [],
        op: 'MODIFY',
        oldSnapshot: '723012',
        newSnapshot: '948053',
        myChange: false
      }
    ],
    id: 'F5ZGK4DPF5CHS3TBNVUWG4ZPIJ2XG2LOMVZXGICTMVZHM2LDMUXVGQSSIYQESQKOKVASARKGKMQEGQZAJFXHIZLHOJQXI2LPNY======',
    objectId: '30263',
    group: 'repo',
    category: 'Dynamics',
    type: 'Business Service'
  }
];

// define a function that describes how a child should merge with its parent
function merge(p) {
  // p is the parent object
  // c are all of p's children defined in items
  const child = p['items'];
  // on the item that we return we only want keys that appear in the parent
  // create an array for the flattened results
  const f = [];
  child.forEach(el => {
    let keys = Object.keys(p);
    // clone the parent object
    let c = Object.assign({}, p);
    // basically unset the items since we are already iterating of them
    child['items'] = [];
    // now iterate over all of the childrens attrs described by the parents parameters
    keys.forEach(k => {
      // the childs value for each key
      const val = el[k];
      if (val !== undefined && val !== null) {
        // handle your special cases in here, such as the name
        if (k === 'name') {
          c['name'] = '[' + el['name'] + '] ' + c['name'];
        } else {
          // default behavior for the non specific cases
          c[k] = el[k]
        }
      }
    });
    // add the object to the array of flattened children
    f.push(c);
  });
  return f;
}

// iterate over all of the parents and their children
// to do this we want to apply a map to all of the parents
const m = p.map(el => merge(el));

JS Bin of the example merge function.

As far as I know, you there isn't really a good reason to use something like lodash (_) or ramda for this, since it isn't a straight forward flatten/merge. You are wanting to do specific things depending on the key, so you will have to roll out a custom function to handle it.

The code that I provided is heavily documented and relatively straight forward, but for learnings sake here is a high level overview:

  • You have an array of objects, where each object is a parent to N children.
  • We want to merge each child with it's parent
  • Based on the example output, we only want to include the object's attributes that exist in the parent object, and the child attribute values will only override the parent values if they are not null or undefined
  • There needs to be custom override behavior depending on which attribute is being merged

Upvotes: 0

Related Questions