0

What is a good way to iterate over a js array of objects, and then within each object, check a value in the object against all others for uniqueness, and push to a new array.

I have an array of objects:

const weatherArray = [
  {
    dt: 1526871600
    dt_txt: "2018-05-21 03:22:00"
  },
  {
    dt: 1526871600
    dt_txt: "2018-05-22 03:30:00"
  },
  {
    dt: 1526871600
    dt_txt: "2018-05-21 03:50:00"
  },
  {
    dt: 1526871600
    dt_txt: "2018-05-23 03:17:00"
  },
  {
    dt: 1526871600
    dt_txt: "2018-05-23 03:23:00"
  }
]

I need to check each object and if the dt_txt (just date, not time eg: 2018-05-23, 2018-05-21 etc) is unique, push that object into a new array.

Below is what I have been trying, I commented to the code to show my train of thought.

var uniqueDays = []
function getDays(weatherArray) {
  // push first value to new array to compare against other items
  uniqueDays.push(weatherArray[0])
  // get just the yyyy-mm-dd from txt we're comparing against
  let firstDayString = weatherArray[0].dt_txt.split(" ")[0]

  weatherArray.map((day) => {
    let dayString = day.dt_txt.split(" ")[0]

    uniqueDays.map((uniqueDay, index) => {
      // get just the yyyy-mm-dd for new array items
      let unqiueDayString = uniqueDay.dt_txt.split(" ")[0]

      // if the value exists, do nothing
      if (unqiueDayString == dayString) {
        console.log('duplicate');
      } else {
        // otherwise push to new array (this is the issue)
        uniqueDays.push(day)
      }
    })
  })

  return uniqueDays
}

The issue I am having is that pushing to unique day's within it's own map function is causing a recursion issue. I know there has to be a better way to this. Any help or direction would be a big relief, I've been struggling with this for some time.

3 Answers 3

3

const weatherArray = [
  {
    dt: 1526871600,
    dt_txt: "2018-05-21 03:22:00"
  },
  {
    dt: 1526871600,
    dt_txt: "2018-05-22 03:30:00"
  },
  {
    dt: 1526871600,
    dt_txt: "2018-05-21 03:50:00"
  },
  {
    dt: 1526871600,
    dt_txt: "2018-05-23 03:17:00"
  },
  {
    dt: 1526871600,
    dt_txt: "2018-05-23 03:23:00"
  }
]

const seen = {};
const res = weatherArray.filter(function(dt){
  const date = dt.dt_txt.substring(0, 10);
  if (!seen[date]) {
    seen[date] = true;
    return true;
  }
});

console.log(res);

note that it's not clear how robust you need to parse your dates. Looking at your example all dates are in the format YYYY-MM-DD so I just used substring which would be faster than split or a regular expression.

As for why your code doesn't work it's because you're looping over uniqueDays but also pushing to it inside the loop. That's a no-no.

The code you posted is parsing the dates in uniqueDays every time. For a large collection that would be slow. The solution above only needs to parse the dates once per item so that would be faster.

Your code uses map but map generates a new array from whatever the function passed to map returns. In your case it's generating an array of undefined as in [undefined, undefined, undefined] and then throwing that array away. You probably wanted to use forEach.

I'm assuming you looped because you needed to keep parsing the dates. If you didn't parse the dates you can use someArray.indexOf to find if there is an entry in an array as in

const isInArray = someArray.indexOf(value) >= 0; 

You can also use someArray.findIndex which takes a function so you could have used that to parse the dates.

But, finally, looking through an array is slow, checking if a property exists in an object is fast. Because of the differences between arrays and objects the algorithm for finding a property, a hashmap, is O(1) where as to find an value in an unsorted array is O(N/2).

Let me add that @CertainPerformance's solution is also elegant. It's collecting the objects in accum and then has to iterate over that object to generate an array when it calls Object.values. For a large result array that would theoretically be slower than not having that second hidden loop.

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

2 Comments

such a beautiful function. thank you for your generous help
Just studied this again and wanted to say thanks again for taking the time. You really helped me grow here.
3

You can group objects in an array with .reduce, putting each unique date into an object indexed by date and then getting that object's values:

const weatherArray=[{dt:1526871600,dt_txt:"2018-05-21 03:22:00"},{dt:1526871600,dt_txt:"2018-05-22 03:30:00"},{dt:1526871600,dt_txt:"2018-05-21 03:50:00"},{dt:1526871600,dt_txt:"2018-05-23 03:17:00"},{dt:1526871600,dt_txt:"2018-05-23 03:23:00"}];

const output = 
  Object.values(
    weatherArray.reduce((accum, obj) => {
      const date = obj.dt_txt.slice(0, 10);
      if (!accum[date]) accum[date] = obj;
      return accum;
    }, {})
  );
console.log(output);

Comments

0
 const newUniqWeatherArray = [];

weatherArray.forEach(x => {
  const date = x.dt_txt.split(' ')[0];

  if(!newUniqWeatherArray.some((x) => x.dt_txt.split(' ')[0] === date)){
    newUniqWeatherArray.push(x);
  }
})

Basically the pseudocode is as below:

  • Loop thru weatherArray
  • For each of the child, check if the date exist in new array
    • If not exist, push this object into the new array
    • else do nothing

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.