teebeetee
teebeetee

Reputation: 549

javascript create array of objects with a fixed length based on another bigger array

Given an array of an even length:

const items = [
  'this/is/path1',
  'this/is/path2',
  'this/is/path3',
  'this/is/path4',
  'this/is/path5',
  'this/is/path6'
];

i want to create an array of objects that will have certain length objnb. Based on this length, i will divide the above items into chunks, and then store the first index on a new object property path, and the following elements in other1, other2; The same for the other object chunks.

My solution is hacky:

const objnb = 2;
const other1 = true;
const other2 = true;
const outputobjs = items.length / objnb;
const result = items.map((entry, index) => {
  let obj = {};
  console.log({ entry, index })
  if (index % outputobjs === 0) {
    obj.path = entry;
    obj.others = {};
    if (other1) {
      obj.others.two = items[index + 1];
    }
    if (other2) {
      obj.others.three = items[index + 2];
    }

    return obj;
  }

  return obj;
})


console.log('result: ', result)

the output is correct:

[ { path: 'this/is/path1',
    others: { two: 'this/is/path2', three: 'this/is/path3' } },
  {},
  {},
  { path: 'this/is/path4',
    others: { two: 'this/is/path5', three: 'this/is/path6' } },
  {},
  {} ]

but unfortunately, i get empty objects, which i don't want. How can i achieve the same with much cleaner way?

the preferred result will not contain the empty objects.

update

another example is:

const items = [
  'this/is/path1',
  'this/is/path2',
  'this/is/path3',
  'this/is/path4'
];

const objnb = 2;
const other1 = true;
const other2 = false;
const outputobjs = items.length / objnb;
const result = items.map((entry, index) => {
  let obj = {};
  console.log({ entry, index })
  if (index % outputobjs === 0) {
    obj.path = entry;
    obj.others = {};
    if (other1) {
      obj.others.two = items[index + 1];
    }
    if (other2) {
      obj.others.three = items[index + 2];
    }

    return obj;
  }

  return obj;
})


console.log('result: ', result)

and result is:

[ { path: 'this/is/path1', others: { two: 'this/is/path2' } },
  {},
  { path: 'this/is/path3', others: { two: 'this/is/path4' } },
  {} ]

clarification:

every object in the new array will be alike, for example, they will all have path, and since we are dividing the original array evenly, the new objects will have all the same properties. For example, if one has two, the rest of the objects in the new array will all have this property, and if they are supposed to have three, they all will have that property.

the new object will look like:

{ path: 'this/is/path1',
    others: { 
       two: 'this/is/path2', 
       three: 'this/is/path3' // optional; if one object has this, others must have it too.
    }
}

so basically, by dividing the original items array into a specific number, either 2, 3, 4, etc... we will chunk it into smaller arrays, and the new object path will be chunkedArray[0], and if there is one more item in this new chunked array, then two will be chunkedArray[1], and if still one more left, then three will be chunkedArray[2]

so if we divide it by 2, then we will have:

const chunkedArray1 = [
  'this/is/path1', // path
  'this/is/path2', //others.two
  'this/is/path3' //others.three
];

const chunkedArray2 = [
  'this/is/path4',// path
  'this/is/path5',//others.two
  'this/is/path6'//others.three
];

therefore the new objects will have two and three;

but if we divide it into 3, we will have:

const chunkedArray1 = [
  'this/is/path1',// path
  'this/is/path2'//others.two
];

const chunkedArray2 = [
  'this/is/path3',// path
  'this/is/path4'//others.two
];

const chunkedArray3 = [
  'this/is/path5',// path
  'this/is/path6'//others.two
];

so we will have only path and two for each object.

every new chunkedArray will have at least length of two, meaning that path and two is present in every new object.

another example:

and one basic example is, if the original array is three, then we can't divide it evenly to smaller chunks, so :

const items = [
  'this/is/path1', //path
  'this/is/path2',//others.two
  'this/is/path3'//others.three
];

and same here , if the original array is two length:

const items = [
  'this/is/path1', //path
  'this/is/path2',//others.two
];

Upvotes: 0

Views: 102

Answers (3)

Sphinx
Sphinx

Reputation: 10729

Below is one solution which uses reduce.

const items = [
  'this/is/path1',
  'this/is/path2',
  'this/is/path3',
  'this/is/path4',
  'this/is/path5',
  'this/is/path6'
]

function splitItems(data, chunkSize) {
  if (chunkSize < 2) return data // or the special value you'd like.
  const labels = {
    1: 'one',
    2: 'two',
    3: 'three',
    4: 'four'
    // ... others
  }
  return data.reduce((pre, cur, index) => {
    if (index % chunkSize === 0) {
      /* for old question
      pre.push({
        path: cur,
        others: {}
      })*/
      let newItem = {
        path: cur,
        others: {}
      }
      Array.from(Array(chunkSize-1).keys()).map( itemIndex => {
        newItem.others[labels[(itemIndex+1) % chunkSize + 1]] = '' //or other default valu
      })
      pre.push(newItem)
    }
    else {
      pre[pre.length - 1].others[labels[index % chunkSize + 1]] = items[index]
    }
    return pre
  }, [])
}

console.log('@Test Case 1@', splitItems(items, 2), '@@')
console.log('@Test Case 2@', splitItems(items.slice(0, 2), 2), '@@')
console.log('@Test Case 3@', splitItems(items.slice(0, 4), 2), '@@')
console.log('@Test Case 4@', splitItems(items.slice(0, 5), 3), '@@')

// calc the size first, then exec splitItems
function splitByLength(data, numberOfChunks) {
  let chunkSize = Math.round(data.length/3, 0)
  return splitItems(data, chunkSize)
}
console.log('@Test Case 5@', splitByLength(items.slice(0, 5), 3), '@@')

Upvotes: 1

Logar
Logar

Reputation: 1248

Another solution using reduce, a bit different from @sphinx answer, although quite similar :

const objnb = 3;
const otherKeys = ['two', 'three'];

const items = [
  'this/is/path1',
  'this/is/path2',
  'this/is/path3',
  'this/is/path4',
  'this/is/path5',
  'this/is/path6'
];

const outputobjs = Math.ceil(items.length / objnb);

const ar = items.reduce((memo, item, index) => {
  const mod = index % outputobjs
  if(mod === 0)
    memo.push({path: item, others: {}});
  else if(mod <= otherKeys.length)
    Object.assign(memo[memo.length - 1].others, {[otherKeys[mod -1]]: item});
  return memo;
}, []);

console.log(ar);

Upvotes: 0

In short, you can loop every 3th item and push it as an object.

const items = [
  'this/is/path1',
  'this/is/path2',
  'this/is/path3',
  'this/is/path4',
  'this/is/path5',
  'this/is/path6'
];
var result = [];
for(var i = 0; i < items.length; i++){
    if(i%3==0){
      result.push({
          path: items[i],
          others: {two: items[i+1] || null, three: items[i+2] || null}
      })
    }
}

console.log(result)

Upvotes: 0

Related Questions