Home > Software engineering >  How to map or assign an entry to an array-item based on some of this item's conditions?
How to map or assign an entry to an array-item based on some of this item's conditions?

Time:02-25

I have array of objects,

if the name is xx then push xitems to that object and

if the name is yy then push yitems to that object

Below is the code tried , and also should not use spread operator

const result = [];
var ss=arrobj.forEach(function(e){
 if(e.name === 'xx'){
   result.push({id: e.id, name: e.name, country:e.country, others: xitems})
 }
if(e.name === 'yy'){
  result.push({id: e.id, name: e.name, country:e.country,  others: yitems})
 }
return result;
});

var arrobj =[
  {id:1, name: "xx", country: "IN"},
  {id:2, name: "yy", country: "MY"},
]

xitems =[
 {title: "Finance", valid: true}
]

yitems =[
 {title: "Sales", valid: true}
]

Expected Output
[
  {id:1, name: "xx", country: "IN", 
   others:[
   {title: "Finance", valid: true}
   ]
  },
  {id:2, name: "yy", country: "MY", 
    others: [
      {title: "Sales", valid: true}
     ]
  },
]


CodePudding user response:

You should use .map for this.

const arrobj = [
  { id: 1, name: "xx", country: "IN" },
  { id: 2, name: "yy", country: "MY" },
];

const xitems = [{ title: "Finance", valid: true }];

const yitems = [{ title: "Sales", valid: true }];

const result = arrobj.map((item) => {
  if (item.name === "xx") {
    item.others = xitems;
  } else if (item.name === "yy") {
    item.others = yitems;
  }

  return item;
});

console.log(result);

CodePudding user response:

Your code works, the only issue that I identified are.

  • There is no need to assign var ss with arrobj.forEach. Because Array.forEach donot return a value.
  • No need of return result; inside Array.forEach.
  • Also as an improvement you can simply assign the object with key others like Object.assign({}, e, { others: xitems }), rather than returning individual key value.

Working Fiddle

const arrobj = [
  { id: 1, name: "xx", country: "IN" },
  { id: 2, name: "yy", country: "MY" },
]

const xitems = [
  { title: "Finance", valid: true }
]

const yitems = [
  { title: "Sales", valid: true }
]

const result = [];
arrobj.forEach(function (e) {
  if (e.name === 'xx') {
    result.push(Object.assign({}, e, { others: xitems }))
  }
  if (e.name === 'yy') {
    result.push(Object.assign({}, e, { others: yitems }))
  }
});
console.log(result)

CodePudding user response:

I would choose a map based approach as well but without the if clauses which explicitly check for expected values of the mapped item's name property.

The approach instead utilizes map's 2nd thisArg parameter which gets applied as the reducer functions this context. Such an additional object can be provided as a map/index of custom key value pairs where key equals a mapped item's name.

Thus the reducer implementation features generic code, and due to the this binding it will be provided as function statement which makes it also re-usable and, if properly named, readable / comprehensible too.

function assignBoundNamedValueAsOthers(item) {
  // the bound key value pairs.
  const index = this;

  // create new object and assign, according to
  // `item.name`, bound named value as `others`.
  return Object.assign({}, item, {
    others: index[item.name] ?? []
  });
}

const arrobj = [
  { id: 1, name: "xx", country: "IN" },
  { id: 2, name: "yy", country: "MY" },
];
const xitems = [{ title: "Finance", valid: true }];
const yitems = [{ title: "Sales", valid: true }];

const result = arrobj
  .map(assignBoundNamedValueAsOthers, {
    // each `key` equals an expected item's `name`.
    xx: xitems,
    yy: yitems,
  });

console.log({
  result,
  arrobj,
  xitems,
  yitems,
});
.as-console-wrapper { min-height: 100%!important; top: 0; }

CodePudding user response:

The example below has been made reusable as long as you meet these requirements:

  • Must have an array of objects as a primary parameter.
  • Must have at least one array of objects for each object in the primary array. If there's more the rest will be ignored.
  • The secondary array of objects must be in the order you want then to end up as.

The second parameter is a rest parameter (not a spread operator, although I have no idea why OP does not want to use it). This will allow us to stuff in as many object arrays as we want.

const distOther = (main, ...oAs) => {...

Next we create an array of pairs from all of the secondary arrays

let others = oAs.map(sub => ['others', sub]);
// [['others', [{...}]], [['others', [{...}]], ...]

Then we turn our attention to the primary array. We'll work our way from the inside out. .map() each object as an array of pairs by Object.entries():

  main.map((obj, idx) => 
//  ...
    Object.entries(obj)
//  ...
// [{A: 1, B: 2}, {...}] => [[['A', 1], ['B', 2]], [[...], [...]]]

Then .concat() (a spead operator would be more succinct) each array of pairs with that of the secondary array of pairs corresponding to the current index (you'll need to wrap each secondary array in another array, so the return will level off correctly):

// main.map((obj, idx) => 
//   ...
//   Object.entries(obj)
     .concat([others[idx]])));
// [[['A', 1], ['B', 2], ['others', [{...}]], [[...], [...], ['others', [{...}]]]

Finally we'll use Object.fromEntries() to convert each array of pairs into an object.

// main.map((obj, idx) =>
     Object.fromEntries(
//   Object.entries(obj)
//   .concat([others[idx]])));
// [{'A': 1, 'B': 2, 'others': [{...}]},...] 

const objArr =[
  {id:1, name: "xx", country: "IN"},
  {id:2, name: "yy", country: "MY"},
];

const x =[
 {title: "Finance", valid: true}
]

const y =[
 {title: "Sales", valid: true}
]

const distOther = (main, ...oAs) => {
let others = oAs.map(sub => ['others', sub]);

return main.map((obj, idx) =>
Object.fromEntries(
Object.entries(obj)
.concat([others[idx]])));
};

console.log(distOther(objArr, x, y));

  • Related