0

I'm struggling to find a good solution to process my array of objects.

I have two arrays:

let structure = ["horizontal","vertical","small","small","small"]

let items = [{"id":1,"title":"xxxxx","format":"horizontal","position":0}, 
            {"id":3,"title":"xxxxx","format":"vertical","position":1}, 
            {"id":6,"title":"xxxxx","format":"small","position":2}, 
            {"id":9,"title":"xxxxx","format":"small","position":3}, 
            {"id":11,"title":"xxxxx","format":"small","position":4}]

Edit: Items are more complex than this: it has about 15 attributes...

structure has a dynamic length and is my reference array. When I change structure I must remap the items array changing the format according to structure. So if I change structure to

let structure = ["horizontal","vertical","vertical","vertical","small"]

The array must change to

 let items = [{"id":1,"title":"xxxxx","format":"horizontal","position":0}, 
              {"id":3,"title":"xxxxx","format":"vertical","position":1}, 
              {"id":6,"title":"xxxxx","format":"vertical","position":2}, 
              {"id":9,"title":"xxxxx","format":"vertical","position":3}, 
              {"id":11,"title":"xxxxx","format":"small","position":4}]

This can be done with a map. This is my Vue method, I map the structure and use the function changeStructure I change the format.

methods: {
      changeStructure(object,structure) {
        object.format = structure
        return object
      },
      updateCoverElements() {
        let structure = this.coverTypes[this.currentCoverVersion]
        let elements = this.coverElements
        let changeStructure = this.changeStructure

        let revisedElement = structure.map(function(structure, index) {
          return changeStructure(elements[index],structure)
        });
        console.log(revisedElement);
      }
    },

But the problem is that, as I told before, structure has a dynamic length.

So when I change to

let structure = ["horizontal","vertical","vertical"]

Item results must be

let items = [{"id":1,"title":"xxxxx","format":"horizontal","position":0}, 
            {"id":3,"title":"xxxxx","format":"vertical","position":1}, 
            {"id":6,"title":"xxxxx","format":"vertical","position":2}]

This is not a problem, if the new structure length has less elements.

But when I change to

let structure = ["horizontal","vertical","vertical","vertical","vertical","vertical","vertical"]

Item results must be

let items = [{"id":1,"title":"xxxxx","format":"horizontal","position":0}, 
             {"id":3,"title":"xxxxx","format":"vertical","position":1}, 
             {"id":6,"title":"xxxxx","format":"vertical","position":2},
             {"id":"","title":"","format":"vertical","position":3}, 
             {"id":"","title":"","format":"vertical","position":4},
             {"id":"","title":"","format":"vertical","position":5}, 
             {"id":"","title":"","format":"vertical","position":6}]

And here is the problem: I cannot find a good way to dynamically create an object with the same identical structure as other items objects (a copy), with every field empty except for position, the index of the array, and format.

7
  • This seems like an unusual design decision--why are you keeping those objects with empty id and title attributes in your bottom-most example? Commented May 5, 2019 at 14:02
  • I'm creating some empty slots, later with drag & drop I will drop some "contents" and I fill the id and the title Commented May 5, 2019 at 14:07
  • Why not just remove the items from the array when structure is short, then grow the array when structure is long? A clean solution to this problem might involve refactoring some of that contextual code... Commented May 5, 2019 at 14:08
  • When the items are short they are auto removed from the structure, because If I map structure (lenght 4) and items are 5 last item is auto-dropped. My issue is structure lenght > item lenght Commented May 5, 2019 at 14:11
  • I just updated my answer to handle cases where structure is < and > your items. That said, this design seems a little strange to me still, so you might want to consider showing it to some other JS devs you know to get their thoughts... Commented May 5, 2019 at 14:19

3 Answers 3

2

You could use the spread syntax like this. If items has a value at the index, it will overwrite the default id and title values.

let structure = ["horizontal","vertical","vertical","vertical","vertical","vertical","vertical"]

let items = [{"id":1,"title":"xxxxx","format":"horizontal","position":0}, 
            {"id":3,"title":"xxxxx","format":"vertical","position":1}, 
            {"id":6,"title":"xxxxx","format":"vertical","position":2}]

const defaultObj = { id: '', title: '' }

const newItems = structure.map((format, position) => {
  return { ...defaultObj, ...items[position], format, position }
})

console.log(newItems)

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

10 Comments

But then if the attributes of items change (e.g. title gets changed to itemTitle) he has to change this function as well :/
@duhaime There is no indication in the question for any of that. We answer to the minimal reproducible example provided in the question. This answer works for when structure is bigger or smaller than items
Sure, but I'd say one should also try to provide non-brittle code when possible. I mention the note above in case it may be relevant to the OP...
@duhaime From what I see, OP wants to update format and position of items based on structure (And your answer doesn't update position when structure's length is > items)
@RobertoPezzali does the update work? You can create a default obejct with all the properties you need. Then use the spread syntax to merge defaultObj and items[index] if it exists
|
1

Just slice off a new copy of items with max structure.length items, then iterate through your new clone of items and set each format attribute. Finally, create new objects for any elements in structure that don't have a corresponding partner in items:

var structure = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']

var items = [
  {'id':1,'title':'xxxxx','format':'horizontal','position':0}, 
  {'id':3,'title':'xxxxx','format':'vertical',  'position':1}, 
  {'id':6,'title':'xxxxx','format':'vertical',  'position':2},
];

// update all extant items
var _items = Object.assign([], items.slice(0, structure.length));
_items.forEach(function(i, idx) { i.format = structure[idx] });

// create any new items
for (var i=_items.length; i<structure.length; i++) {
  _items.push(Object.assign({}, items[0], {
    id: '',
    title: '',
    position: i,
    format: structure[i],
  }))
}

console.log(_items)

4 Comments

I try but if structure lenght is 7 _items remain 5. Is there something wrong?
Check out the updates (I hadn't read the second half of your question). I'd recommend learning some of these primitive operations in JS before rushing to fancy ES6 notation. Once you have a vocabulary of basic operations in JS you'll be able to use ES6 as a shortcut, but you'll be able to spell out more elaborate operations nice and simply...
ok, I thing this approach is ok. By the way my item object is more complex, about 15 attributes.
Yes, check out Object.assign. It will create a new object of type {{arg 0}}, then copy all attributes and values from {{arg 1}}, then override any attributes and values from {{arg 2}}: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…. So you can copy all boilerplate attributes from some object. If you need greater help with this task, though, I'd ask another question (after reading the MDN docs and giving Object assign a whirl)
0

The best way to solve this problem using map() method would be to organize your implementation functional so that you could call it many times with a different set of data wherever you need (the concept of reusability).

Here is what I have tried. I have defined the function named getAlteredArray(items, structure) which returns the desired array. Note that if you are looking to use map() method means you want a new array (I mean you don't want to alter the passed array).

I have pasted the o/p on the code itself (it is commented out).

// function that creates new array with desired items and returns to the caller
function getAlteredArray(items, structure) {
    let newArr = items.map((obj, index) => {
        // obj => {"id":1,"title":"xxxxx","format":"horizontal","position":0}
        obj["format"] = structure[index]
        return obj
    })

    return newArr
}

// function that creates set of data needs to be passed to getAlteredArray() function
function test() {
    let items = [{"id":1,"title":"xxxxx","format":"horizontal","position":0}, 
            {"id":3,"title":"xxxxx","format":"vertical","position":1}, 
            {"id":6,"title":"xxxxx","format":"small","position":2}, 
            {"id":9,"title":"xxxxx","format":"small","position":3}, 
            {"id":11,"title":"xxxxx","format":"small","position":4}]

    // TEST 1 - Call getAlteredArray() with `items` & `structure`
    let structure = ["horizontal","vertical","vertical","vertical","small"]
    let newArr = getAlteredArray(items, structure)
    console.log(newArr)
    /*
        [ { id: 1, title: 'xxxxx', format: 'horizontal', position: 0 },
          { id: 3, title: 'xxxxx', format: 'vertical', position: 1 },
          { id: 6, title: 'xxxxx', format: 'vertical', position: 2 },
          { id: 9, title: 'xxxxx', format: 'vertical', position: 3 },
          { id: 11, title: 'xxxxx', format: 'small', position: 4 } ]
    */

    // TEST 2 
    let structure2 = ["horizontal","vertical","small","small","small"]
    let newArr2 = getAlteredArray(items, structure2)
    console.log(newArr2)
    /*
        [ { id: 1, title: 'xxxxx', format: 'horizontal', position: 0 },
          { id: 3, title: 'xxxxx', format: 'vertical', position: 1 },
          { id: 6, title: 'xxxxx', format: 'small', position: 2 },
          { id: 9, title: 'xxxxx', format: 'small', position: 3 },
          { id: 11, title: 'xxxxx', format: 'small', position: 4 } ]    
    */
}

// Start
test()

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.