0

This answer is already close, and there are some answers how to get unique values in an array (remove duplicates,)though I can't make it work for the case where it is about an array of objects, and the property that should be filtered is an array. Sorry, I am a JS newbie. Thanks for the help.

I have an array of objects like this

const posts = [
  post1: {
    id: 1,
    title: 'One',
    tags: ['tagA', 'tagB']
    },
  post2: {
    id: 2,
    title: 'Two',
    tags: ['tagB', 'tagC']    
    },
  post3: {
    id: 3,
    title: 'Three',
    tags: ['tagB', tagC, tagD]    
    ]

What I would need is an array of all unique tags ... in the case above with an expected output like this:

// [tagA, tagB, tagC, tagD] 

EDIT / UPDATE

The key in the array of objects is used to manage the state of the react component... e.g.

constructor(props) {
super(props);

this.state = {
  posts: []
};
}

...

updatePost = (key, updatedPost) => {
//1. Take copy of the current this.state.
const posts = {...this.state.texts};
//2. Update that state
posts[key] = updatedPost;
//3. Set that to state
const options = { encrypt: false }
putFile(postsFileName, JSON.stringify(posts), options)
  .then(() => {
    this.setState({
      posts: posts
    })
  })
  };
YvonC
  • 329
  • 2
  • 6
  • 22
  • 1
    Your posts are not valid JS, should it be directly object in your array? – Axnyff Jun 26 '18 at 17:06
  • 1
    This is not a valid array. – zfrisch Jun 26 '18 at 17:08
  • In case you don't get what they are saying, `posts` is an array, `post1`, `post2`, `post3`, would be keys in an object, not an array. Did you copy over something incorrectly? – Ted Jun 26 '18 at 17:10
  • Sorry, guys, apologies if I use the wrong terms here. This what I mentioned as "array of objects", is the state that I get in my react component, after adding posts. The key is added to manage / update the posts... see the "edit / update" above. – YvonC Jun 26 '18 at 17:24

5 Answers5

3

Assuming that the input is on [ {} , {} ] format:

You can use concat and map to flatten your array. Use new Set to get the unique values.

const posts = [{"id":1,"title":"One","tags":["tagA","tagB"]},{"id":2,"title":"Two","tags":["tagB","tagC"]},{"id":3,"title":"Three","tags":["tagB","tagC","tagD"]}];

var result = [...new Set([].concat(...posts.map(o => o.tags)))];

console.log(result);

If the variable is an object ( {a:{} , b:{} } ) , you can use Object.values to convert the object into an array.

const posts = {"post1":{"id":1,"title":"One","tags":["tagA","tagB"]},"post2":{"id":2,"title":"Two","tags":["tagB","tagC"]},"post3":{"id":3,"title":"Three","tags":["tagB","tagC","tagD"]}}

var result = [...new Set([].concat(...Object.values(posts).map(o => o.tags)))];

console.log(result);
Eddie
  • 26,593
  • 6
  • 36
  • 58
  • 1
    I want to upvote this, but it really depends on if OPs dataset is supposed to be an Object or an Array. – zfrisch Jun 26 '18 at 17:17
0

You can reduce your posts and iterate over the tags and push those to the result that you haven't encountered already:

const posts = [
  {
    id: 1,
    title: "One",
    tags: ["tagA", "tagB"]
  },
  {
    id: 2,
    title: "Two",
    tags: ["tagB", "tagC"]
  },
  {
    id: 3,
    title: "Three",
    tags: ["tagB", "tagC", "tagD"]
  }
];

const uniqueTags = posts.reduce((result, post) => {
  post.tags.forEach(tag => {
    if (!result.includes(tag)) {
      result.push(tag);
    }
  });

  return result;
}, []);

console.log(uniqueTags);
Tholle
  • 108,070
  • 19
  • 198
  • 189
0

This is assuming you know that the array key is always 'tags'.

let filter = {};
let result = [];

posts.forEach(post => {
  const tags = post['tags'];
  tags.forEach(tag => {
    if (!filter.hasOwnProperty(tag)) {
      result.push(tag);
      filter[tag] = true; 
    }
  });
});
0

with jquery you can do something similar to this (not Tested):

var results = [];
$.each(myObject, function(key,valueObj){
    var check.isArray(obj);
    if(check){
        alert(key + "/" + valueObj );
        /*replace repeat*/
        var sorted_check = check.slice().sort(); // You can define the comparing function here. 
                                     // JS by default uses a crappy string compare.
                                     // (we use slice to clone the array so the
                                     // original array won't be modified)

        for (var i = 0; i < sorted_check.length - 1; i++) {
            if (sorted_check[i + 1] == sorted_check[i]) {
                results.push(sorted_check[i]);
            }
        }
    }
});

and a good way with indexof:

Array.prototype.unique = function() {
    var a = [];
    for ( i = 0; i < this.length; i++ ) {
        var current = this[i];
        if (a.indexOf(current) < 0) a.push(current);
    }

    this.length = 0;
    for ( i = 0; i < a.length; i++ ) {
        this.push( a[i] );
    }

    return this;
}

Array.prototype.unique = function() {
    var a = [];
    for ( i = 0; i < this.length; i++ ) {
        var current = this[i];
        if (a.indexOf(current) < 0) a.push(current);
    }
    return a;
}

And Follow UP:

Array.prototype.unique = function(mutate) {
    var unique = this.reduce(function(accum, current) {
        if (accum.indexOf(current) < 0) {
            accum.push(current);
        }
        return accum;
    }, []);
    if (mutate) {
        this.length = 0;
        for (let i = 0; i < unique.length; ++i) {
            this.push(unique[i]);
        }
        return this;
    }
    return unique;
}
0

If you want to use a functional library like Ramda.js you can do this:

const posts = [
 {
  id: 1,
  title: 'One',
  tags: ['tagA', 'tagB'],
 },
 {
  id: 2,
  title: 'Two',
  tags: ['tagB', 'tagC'],
 },
 {
  id: 3,
  title: 'Three',
  tags: ['tagB', 'tagC', 'tagD'],
 },
];

var unique = R.uniq(R.flatten(R.map(R.prop('tags'), posts)))
console.log(unique)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>
Morris S
  • 2,337
  • 25
  • 30