1

I have a an array of objects in this format:

const myArray = [
 {
  one: 'A',
  two: 'AB'
 },
 {
  one: undefined,
  two: 'AB'
 },
 {
  one: 'A',
  two: 'BC'
 },
 {
  one: undefined,
  two: 'CC'
 },
]

I want the output in this particular format where if one is undefined but two is present in another object then ignore that object. e.g., output:

const outputMyArray = [
 {
  one: 'A',
  two: 'AB'
 },
 {
  one: 'A',
  two: 'BC'
 },
 {
  one: undefined,
  two: 'CC'
 },

The object keys are dynamically generated hence cannot be hardcoded and each object can have same numbers of keys as others. This I have tried but did not work:

const objectValuesList = myArray.map((obj: any) => Object.values(obj))

const uniqueStrigifiedList = Array.from(new Set( objectValuesList.filter((obj: any) => !obj.includes(undefined)).map((obj: any) => JSON.stringify(obj)) ))

myArray?.filter((obj: any) => !uniqueStrigifiedList?.includes( String(Object.values(obj)) ))

This solution does not seem to be working as it considers all object keys as unique.

1
  • im not quite sure i understand. why cant you use filter on outputMyArray directly
    – cmgchess
    Commented Mar 20, 2023 at 18:36

5 Answers 5

0

You could take a two step approach:

  1. Get all object with no undefined as value and store the values with to an object.
  2. Check the objects with undefined and if not seen add this object to the result array.

const
    data = [{ one: 'A', two: 'AB' }, { one: undefined, two: 'AB' }, { one: 'A', two: 'BC' }, { one: undefined, two: 'CC' }],
    keys = ['one', 'two'],
    result = [],
    seen = Object.fromEntries(keys.map(k => [k, {}])),
    withUndefined = [];

for (const o of data) {
    if (keys.every(k => o[k] !== undefined)) {
        keys.forEach(k => seen[k][o[k]] = true);
        result.push(o);
    } else {
        withUndefined.push(o);
    }
}

for (const o of withUndefined) {
    if (keys.some(k => seen[k][o[k]])) continue;
    result.push(o);
}

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

0

This should be doable with one filter:

  • If there is no one,

    • and if there isn't any other element in the array with the same two and a non-undefined one,

      • then it fails the filter (removed)

      • else, it passes (kept)

  • Otherwise, it passes the filter (kept)

const myArray=[{one:'A',two:'AB'},{one:undefined,two:'AB'},{one:'A',two:'BC'},{one:undefined,two:'CC'},];

const output = myArray.filter((value) => !value.one ? !myArray.some((other) => other !== value && other.two === value.two && other.one) : true);

console.log(output);

0

I tried this and it worked with me

const outputMyArray = myArray.filter((obj, index) => {
    if (!obj.one) {
        let flag = true;
        for (let i = 0; i < myArray.length; i++) {
            if (index == i) continue;
            if (obj.two == myArray[i].two) flag = false;
        }
        return flag
    } else return true
});

Maybe it isn't the best practices but I tried to help

1
  • You should return true instead of return obj, and an explicit return false would be nice as well.
    – k.tten
    Commented Mar 20, 2023 at 18:37
0
// Define the type to indicate the shape of the objects
const myArray: Array<Record<string, string | undefined>> = [
    { one: 'A', two: 'AB' },
    { one: undefined, two: 'AB' },
    { one: 'A', two: 'BC' },
    { one: undefined, two: 'CC'},
]

const outputMyArray = myArray.filter(obj => {
    // Get the keys for each object
    const keys = Object.keys(obj);

    // What would you do in the case of empty objects?
    if (keys.length === 0) { 
        return false;
    } else {
        // Using 'for of' loop to get each key value for each object within the array.
        for (const key of keys) {
            if (obj[key] === undefined) {
                // once there's one undefined key 
                return false; 
            } 
        }
        // All the keys were not undefined
        return true;
    }
})

console.log(outputMyArray);
0

Reduce the array to a new array. For each object in the array check for a matching object in the accumulator (acc), and if found replace it. If not, add the current item to the accumulator:

const { isMatchWith, isUndefined } = _

const data = [{ one: 'A', two: 'AB' }, { one: undefined, two: 'AB' }, { one: 'A', two: 'BC' }, { one: undefined, two: 'CC' }]

// match if values are identical or if the 2nd value is undefined
const isMatchWithUndefined = (o1, o2) => 
  isMatchWith(o1, o2, (_, v2) => isUndefined(v2) || undefined)

// find and return the best match (less undefined) and the index
const findMatch = (arr, o1) => {
  for(let i = 0; i < arr.length; i++) {
    const o2 = arr[i]
    if(isMatchWithUndefined(o1, o2)) return [o1, i]
    if(isMatchWithUndefined(o2, o1)) return [o2, i]
  }
  
  return [null, -1]
}

const result = data.reduce((acc, o) => {  
  const [match, index] = findMatch(acc, o)
  
  if(match) acc[index] = match // if there's a match set the match at the index
  else acc.push(o) // if not add the item to the accumulator
  
  return acc
}, [])

console.log(result)
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js" integrity="sha512-WFN04846sdKMIP5LKNphMaWzU7YpMyCU245etK3g/2ARYbPK9Ub18eG+ljU96qKRCWh+quCY7yefSmlkQw1ANQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.