1

I am working on ReactJS project and have the following JSON data:

[
  {
    "sources": [
      {
        "sourceName": "test_demographics",
        "table": "test_demographics_summary"
      }
    ],
    "userId": "test",
    "queryType": {
      "mixedQuery": false,
      "queryTable": "test_a_query"
    }
  },
  {
    "sources": [
      {
        "sourceName": "something_demographics",
        "table": "something_demographics_summary"
      },
      {
        "sourceName": "something_demographics",
        "table": "something_demographics_summary"
      }
    ],
    "userId": "test",
    "queryType": {
      "mixedQuery": false,
      "queryTable": "test_bquery"
    }
  }
]

I want to extract all the objects in the sources property into one array with deduped data. So essentially for the above JSON data I want an array like so:

[
  {
    "sourceName": "test_demographics",
    "table": "test_demographics_summary"
  },
  {
    "sourceName": "something_demographics",
    "table": "something_demographics_summary"
  }
]

Appreciate any suggestions.

2
  • 2
    What determines they are duplicates? All key/values match? Commented Dec 4, 2018 at 16:34
  • 2
    Can we see your attempt to solve? Commented Dec 4, 2018 at 16:39

3 Answers 3

2

Since javascript compares objects by reference you need to invent a way to compare by value. One way is to serialize the objects and store the serialized version in a set. Here you can serialize by taking the Object.entries(), sorting on keys, and JSON.stringify them. Once you've done that, it's just a matter of looping through and adding what you haven't seen to the set and output. Something like:

let arr = [{"sources": [{"sourceName": "test_demographics","table": "test_demographics_summary"}],"userId": "test","queryType": {"mixedQuery": false,"queryTable": "test_a_query"}},{"sources": [{"sourceName": "something_demographics","table": "something_demographics_summary"},{"sourceName": "something_demographics","table": "something_demographics_summary"}],"userId": "test","queryType": {"mixedQuery": false,"queryTable": "test_bquery"}}]

function getUniqueSources(arr){

    let seen = new Set // holds serialized object

    return arr.reduce((arr, item) => {
        item.sources.forEach(source =>{
            // need to sort so key order doesn't matter for equality
            let serial = JSON.stringify(Object.entries(source).sort((a, b) => a[0].localeCompare(b[0])))
            if (!seen.has(serial)) {
                seen.add(serial)
                arr.push(source)
            }
        })
        return arr

    },[])
}

console.log(getUniqueSources(arr))

Sign up to request clarification or add additional context in comments.

Comments

1

If you think you would have various data types and the objects in sources wound not have different order of their props then you can use Set with Array.reduce and JSON.stringify to convert them into strings for comparison:

const data = [ { "sources": [ { "sourceName": "test_demographics", "table": "test_demographics_summary" } ], "userId": "test", "queryType": { "mixedQuery": false, "queryTable": "test_a_query" } }, { "sources": [ { "sourceName": "something_demographics", "table": "something_demographics_summary" }, { "sourceName": "something_demographics", "table": "something_demographics_summary" } ], "userId": "test", "queryType": { "mixedQuery": false, "queryTable": "test_bquery" } } ]

const exists = (s, v) => {
  const json = JSON.stringify(Object.entries(v))
  return s.has(json) ? true : (s.add(json), false)
}
const getUniques = arr => {
  let merged = data.reduce((r,c) => [...r.sources, ...c.sources]), s = new Set()	
  return merged.reduce((r,c) => !exists(s, c) ? (r.push(c), r) : r, [])
}
console.log(getUniques(data))

The idea would be to first merge all the sources via ES6 spread and then using the Set reduce them by checking if the string representation of the sources already exists in the set. We only add those which **do not ** exits in the set.

Note that every time you are dealing with JSON.stringify and object comparison you are at the mercy of the order of the properties in the object you are comparing so if object A is {a: 1, b: 2} and object B is {b: 2, a: 1} they would be considered different since they string representation would not match at all. So as long as this is not your scenario you should be good with this.

If however the values in your sources objects are going to be strings only you can also consider using Array.reduce and group by the values and just pick the first array element since you know the others are dubs:

const data = [ { "sources": [ { "sourceName": "test_demographics", "table": "test_demographics_summary" } ], "userId": "test", "queryType": { "mixedQuery": false, "queryTable": "test_a_query" } }, { "sources": [ { "sourceName": "something_demographics", "table": "something_demographics_summary" }, { "sourceName": "something_demographics", "table": "something_demographics_summary" } ], "userId": "test", "queryType": { "mixedQuery": false, "queryTable": "test_bquery" } } ]

const getUniques = arr => {
   const merged = data.reduce((r,c) => [...r.sources, ...c.sources])	
   return Object.values(merged.reduce((r,c) => {
      r[Object.values(c).join()] = c
      return r
   }, {}))
}
console.log(getUniques(data))

Comments

0

I ended up doing the following:

    // extract sources arrays
    let sources = queryObjects.map(function (queryObject) {
        return queryObject.sources;
    })

    // add sources objects into one array and remove duplicates
    let sourcesArray = sources.reduce((acc, currValue) => {
        return _.uniq(acc.concat(currValue));
    })

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.