arunmmanoharan
arunmmanoharan

Reputation: 2675

Find duplicate or falsy value in a JS object - Javascript

I have an object where it can contain a duplicate and/or a falsy value. I want to compose an array of objects based on that and add a new boolean property based on the check for case-insensitive values.

This is what I have:

const obj = {
  a: 'A',
  b: 'B',
  c: 'C',
  d: 'c',
  e: 'E',
  f: ''
}

console.log(Object.keys(obj).map(i => {
  return {
    key: i,
    isDuplicateOrFalsy: _.filter(
        Object.values(obj),
        j =>
        _.trimEnd(_.toLower(j)) ===
        _.trimEnd(
          _.toLower(
            obj[i]
          )
        )
      ).length > 1 ||
      !_.every(
        Object.values(obj),
        Boolean
      )
  }
}))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js"></script>

Expected Output:

[{
  isDuplicateOrFalsy: false,
  key: "a"
}, {
  isDuplicateOrFalsy: false,
  key: "b"
}, {
  isDuplicateOrFalsy: true,
  key: "c"
}, {
  isDuplicateOrFalsy: true,
  key: "d"
}, {
  isDuplicateOrFalsy: false,
  key: "e"
}, {
  isDuplicateOrFalsy: true,
  key: "f"
}]

Please advice.

Upvotes: 2

Views: 169

Answers (5)

Doppio
Doppio

Reputation: 2188

A short, and more human readable.

const obj = {
  a: 'A',
  b: 'B',
  c: 'C',
  d: 'c',
  e: 'E',
  f: ''
}

// Object map number of occurance of each value. { a: 1, b: 1, c: 2, d: 1 }
const valuesOccurance = _.mapValues(_.groupBy(obj, _.lowerCase), occurances => occurances.length);

// function to check duplicate
const isDuplicate = value => valuesOccurance[_.lowerCase(value)] > 1;

// function to check falsy value
const isFalsy = value => !value;

const result = _.map(obj, (value, key) => {
  return {
    isDuplicateOrFalsy: isFalsy(value) || isDuplicate(value),
    key,
  };
});
console.log({ result })
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js"></script>

Upvotes: 1

Ori Drori
Ori Drori

Reputation: 193358

Convert the object to entries of [key, value] with _.toPairs(), and group them by the lower case version of the value. Flat map the groups, and map each entry in the group back to an object. Any item within a group with length greater than 1 is a duplicate. Merge the objects, and get the items in the correct order using _.at():

const fn = obj => _.at(
  _.merge(..._.flatMap(
    _.groupBy(_.toPairs(obj), ([, v]) => _.lowerCase(v)),
    group => group.map(([key, v]) => ( { [key]:{
      key,
      isDuplicateOrFalsy: group.length > 1 || _.isEmpty(_.trim(v))
    }}))
  )),
  _.keys(obj) 
)


const obj = {"a":"A","b":"B","c":"C","d":"C","e":"E","f":"","g":"c"}

const result = fn(obj)

console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js"></script>

Upvotes: 2

Vasilii Kovalev
Vasilii Kovalev

Reputation: 156

const obj = {
  a: 'A',
  b: 'B',
  c: 'C',
  d: 'c',
  e: 'E',
  f: ''
};

const isDuplicateOrFalsyByValue = Object
  .values(obj)
  .reduce(
    (result, value) => {
      const caseInsensetiveValue = value.toLowerCase();
      result[caseInsensetiveValue] = result[caseInsensetiveValue] === undefined
        /*
          * If `caseInsensetiveValue` is a falsy value,
            then set `isDuplicateOrFalsy` to `true`
          * Otherwise set it to `false`
        */
        ? !caseInsensetiveValue
        /*
          * If result[caseInsensetiveValue] is `true` (we had a falsy value),
            then this `true` won't hurt
          * Otherwise we have a duplicate at this point
            and should set it to `true` as well.
        */
        : true;

      return result;
    },
    {},
  );

const keysWithDuplicationOrFalsyInfo = Object
  .entries(obj)
  .reduce(
    (result, [key, value]) => [
      ...result,
      {
        isDuplicateOrFalsy: isDuplicateOrFalsyByValue[value.toLowerCase()],
        key,
      },
    ],
    [],
  );
console.log('keysWithDuplicationOrFalsyInfo');
console.log(keysWithDuplicationOrFalsyInfo);

Upvotes: 1

Daniil Loban
Daniil Loban

Reputation: 4381

Solution does not contain unnecessary cycles:

const obj = {
  a: 'A',
  b: 'B',
  c: 'C',
  d: 'C',
  e: 'E',
  f: ''
}
// make array [ [key, value], ...  ] and sort by values 
values = Object.entries(obj).sort((a,b)  => a[1] > b[1])
result = values.map((e,i, arr) => {
  const [key, value] = e;
  const last = values.length -1; // last index
  let isDuplicateOrFalsy = false;
  
  // true conditions = dublicates are near
  if (!value) isDuplicateOrFalsy = true; // falsy check
  else if (i > 0 && i < last  // for middle
      && (arr[i-1][1] === value || arr[i+1][1] === value))  isDuplicateOrFalsy = true;   
  else if (i === 0 && arr[1][1] === value) isDuplicateOrFalsy = true; // for first
  else if (i === last && arr[last-1][1] === value) isDuplicateOrFalsy = true;   // for last


  return {
    key,
    isDuplicateOrFalsy
  }
})

console.log(result)

Upvotes: 1

blex
blex

Reputation: 25648

You could do something similar to this:

const obj = { a: 'A', b: 'B', c: 'C', d: 'C', e: 'E', f: '' };

const res = Object.entries(obj)
            .map(([key, val], i, arr) => ({
                key,
                isDuplicateOrFalsy: !val ||
                  arr.some(([k, v], j) =>
                    j !== i && v.toLowerCase().trim() === val.toLowerCase().trim()
                  )
            }));
                  
console.log(res);

Upvotes: 1

Related Questions