Jomol MJ
Jomol MJ

Reputation: 691

Filter javascript array based on condition

I am generating a dynamic array like below. What I wanted to achieve is to compare the first set with the second and return the common items based on id into a new array. Also the pending and required value should be grater than 0.

For example, Case : 1

let arr = [
  {
    0: { id: 1, name: "A", required: 1, pending: 1 },
    1: { id: 2, name: "B", required: 0, pending: 0 }
  },
  {
    0: { id: 1, name: "A", required: 1, pending: 1 },
    1: { id: 2, name: "B", required: 1, pending: 1 },
    2: { id: 3, name: "C", required: 0, pending: 0 }
  }
]

The result would be A in this case as follows.

[
{ id: 1, name: "A", required: 1, pending: 1 }
]

Another case, Case : 2 since the array is dynamically generated. So the following can be possible.

let arr = [
      {
        0: { id: 1, name: "A", required: 1, pending: 1 },
        1: { id: 2, name: "B", required: 1, pending: 1 },
        2: { id: 3, name: "C", required: 0, pending: 0 }
      }
    ]

The expected output of this would be

  [
    { id: 1, name: "A", required: 1, pending: 1 },
    { id: 2, name: "B", required: 1, pending: 1 }
  ]

The below function gives the desired output for Case 1. This function not works for Case 2. Can anybody help me to solve this?

let test = arr .reduce((p, c) => p.filter(e =>c.some(s => s.id === e.id && s.pending> 0 && e.pending> 0 && s.required> 0 && e.required> 0)));

Upvotes: 1

Views: 132

Answers (4)

prinz
prinz

Reputation: 148

Can you try this? It's worked for me..

let test = arr.reduce((p, c) => p.filter(e =>c.some(s => s.id === e.id && s.pending> 0 && e.pending> 0 && s.required> 0 && e.required> 0)));

let res = arr.length>1?test:(test.filter(s =>  s.pending> 0 && s.required> 0));

Upvotes: 1

Siva Kondapi Venkata
Siva Kondapi Venkata

Reputation: 11011

Use forEach and push to result array.

const filter = (arr) => {
  const res = [];
  const track = {};
  arr.forEach((obj) =>
    Object.entries(obj).forEach(([i, item]) => {
      const isValid = ["required", "pending"].every((key) => item[key] > 0);
      if (isValid && !(item.name in track)) {
        res.push({ [i]: item });
      }
      track[item.name] = isValid;
    })
  );
  return res;
};

let arr1 = [
  {
    0: { id: 1, name: "A", required: 1, pending: 1 },
    1: { id: 2, name: "B", required: 0, pending: 0 },
  },
  {
    0: { id: 1, name: "A", required: 1, pending: 1 },
    1: { id: 2, name: "B", required: 1, pending: 1 },
    2: { id: 3, name: "C", required: 0, pending: 0 },
  },
];

let arr2 = [
      {
        0: { id: 1, name: "A", required: 1, pending: 1 },
        1: { id: 2, name: "B", required: 1, pending: 1 },
        2: { id: 3, name: "C", required: 0, pending: 0 }
      }
    ]
    
console.log(filter(arr1));
console.log('------------');
console.log(filter(arr2));

Update: Output without indexes.

const filter = (arr) => {
  const res = [];
  const track = {};
  arr.forEach((obj) =>
    Object.entries(obj).forEach(([i, item]) => {
      const isValid = ["required", "pending"].every((key) => item[key] > 0);
      if (isValid && !(item.name in track)) {
        // res.push({ [i]: item });
        res.push(item);
      }
      track[item.name] = isValid;
    })
  );
  return res;
};

let arr1 = [
  {
    0: { id: 1, name: "A", required: 1, pending: 1 },
    1: { id: 2, name: "B", required: 0, pending: 0 },
  },
  {
    0: { id: 1, name: "A", required: 1, pending: 1 },
    1: { id: 2, name: "B", required: 1, pending: 1 },
    2: { id: 3, name: "C", required: 0, pending: 0 },
  },
];

let arr2 = [
      {
        0: { id: 1, name: "A", required: 1, pending: 1 },
        1: { id: 2, name: "B", required: 1, pending: 1 },
        2: { id: 3, name: "C", required: 0, pending: 0 }
      }
    ]
    
console.log(filter(arr1));
console.log('------------');
console.log(filter(arr2));

Upvotes: 0

Sven.hig
Sven.hig

Reputation: 4519

You could use map and use Object.values to get the values of the nested objects and an object to deduplicate

var arr=[{
  0:{id: 1, name: "A", required: 1, pending: 1},
  1:{id: 2, name: "B", required: 0, pending: 0}
  },
  {
  0:{id: 1, name: "A", required: 1, pending: 1},
  1:{id: 2, name: "B", required: 1, pending: 1},
  2:{id: 3, name: "C", required: 0, pending: 0}
  }
  ]


  map1=new Map()
  map2=new Map()

  var res = arr.map(o => Object.values(o).forEach(
  s => { if(s.required > 0 && s.pending > 0 && !map2.get(s.name)) 
          map1.set(s.name,s)
         else map2.set(s.name,s)
  }))
  console.log([...map1.values()])

let arr = [
  {
    0: { id: 1, name: "A", required: 1, pending: 1 },
    1: { id: 2, name: "B", required: 1, pending: 1 },
    2: { id: 3, name: "C", required: 0, pending: 0 }
  }
]


  map1=new Map()
  map2=new Map()

  var res = arr.map(o => Object.values(o).forEach(
  s => (s.required > 0 && s.pending > 0 && !map2.get(s.name)) ? map1.set(s.name,s) :  map2.set(s.name,s)
))
  console.log([...map1.values()])

Upvotes: 1

Mr. Polywhirl
Mr. Polywhirl

Reputation: 48751

Besides your psudo-matrix code, you can create a simple filter predicate function that takes an entry and short-circuits if any of the conditions fail. You need to check for violating condition instead of requested conditions.

Just return true at the very end.

As for your data, since you have an array of objects (which themselves contain objects), you will need to map each group to a reduction so that you can filter-out the key-value pairs where the value (entry) does not meet the criteria of the predicate. Reducing is just mapping and filtering all at once. You need to reduce, because you cannot filter an object by keys.

const data = [{
  0: { id: 1, name: "A", required: 1, pending: 1 },
  1: { id: 2, name: "B", required: 0, pending: 0 }
}, {
  0: { id: 1, name: "A", required: 1, pending: 1 },
  1: { id: 2, name: "B", required: 1, pending: 1 },
  2: { id: 3, name: "C", required: 0, pending: 0 }
}]

const isRequiredAndPendingGreaterThanZero = (entry) => {
  if (entry == null) return false;
  if (entry.required !== 1) return false;
  if (entry.pending < 1) return false;
  return true;
}

console.log(data.map(group => {
  return Object.entries(group).reduce((res, entry) => {
    let [ key, value ] = entry;
    return isRequiredAndPendingGreaterThanZero(value)
      ? { ...res, [key]: value } : res;
  }, {});
}));
.as-console-wrapper { top: 0; max-height: 100% !important; }

Here is a more dynamic version that checks for EVERY valid condition and allows you to define conditions for each key.

This example uses a simplified data structure of an array of objects, but it can easily be adapted to work for any list data structure, grouped or not.

const data = [
  { id: 1, name: "A", required: 1, pending: 1 },
  { id: 2, name: "B", required: 1, pending: 1 },
  { id: 3, name: "C", required: 0, pending: 0 }
];

const isValid = (entry, criteria) => {
  if (entry == null) return false;
  return Object.keys(criteria).every(key => criteria[key](entry[key]));
}

console.log(data.filter(entry => isValid(entry, {
  required : v => v === 1,
  pending  : v => v > 0
})));
.as-console-wrapper { top: 0; max-height: 100% !important; }

Upvotes: 0

Related Questions