2

I am still fairly new to JavaScript and need help with a task I am trying to accomplish.

I have an array of objects like this:

    const data =
{
    "Total_packages": {
        "package1": {
            "tags": [
                "kj21",
                "j1",
                "sj2",
                "z1"
            ],
            "expectedResponse": [
                {
                    "firstName": "Name",
                    "lastName": "lastName",
                    "purchase": [
                        {
                            "title": "title",
                            "category": [
                                "a",
                                "b",
                                "c"
                            ]
                        }
                    ]
                }
            ]
        },
        "package2": {
            "tags": [
                "s2",
                "dsd3",
                "mhg",
                "sz7"
            ],
            "expectedResponse": [
                {
                    "firstName": "Name1",
                    "lastName": "lastName1",
                    "purchase": [
                        {
                            "title": "title1",
                            "category": [
                                "a1",
                                "b1",
                                "c1"
                            ]
                        }
                    ]
                }
            ]
        },
        "package3": {
            "tags": [
                "s21",
                "dsd31",
                "mhg1",
                "sz71"
            ],
            "expectedResponse": [
                {
                    "firstName": "Name2",
                    "lastName": "lastName2",
                    "purchase": [
                        {
                            "title": "title2",
                            "category": [
                                "a2",
                                "b2",
                                "c2"
                            ]
                        }
                    ]
                }
            ]
        },
        "package4": {
            "tags": [
                "s22",
                "dsd32",
                "mhg2",
                "sz72"
            ],
            "expectedResponse": [
                {
                    "firstName": "Name3",
                    "lastName": "lastName3",
                    "purchase": [
                        {
                            "title": "title3",
                            "category": [
                                "a3",
                                "b3",
                                "c3"
                            ]
                        }
                    ]
                }
            ]
        },
        "package5": {
            "tags": [
                "s22",
                "dsd32",
                "mhg2",
                "sz72"
            ],
            "expectedResponse": [
                {
                    "firstName": "Name4",
                    "lastName": "lastName4",
                    "purchase": [
                        {
                            "title": "title4",
                            "category": [
                                "a4",
                                "b4",
                                "c4"
                            ]
                        }
                    ]
                }
            ]
        }
    }
}

    var arrRand = genNum(data, 3);
    console.log(arrRand);

               function genNum(data, loop='') {
                    var list = [];
                    var arrayOfTags = Object.entries(data.Total_packages).reduce((acc, [k, v]) => {
                        if (v.tags) acc = acc.concat(v.tags.map(t => ({tag: t, response: v.expectedResponse})));
                        return acc;
                    }, []);
                    for (var i = 0; i < loop; i++){
                      var randomIndex = Math.floor(Math.random() * arrayOfTags.length);
                    var randomTag = arrayOfTags[randomIndex];
                    list.push(randomTag);
                    }
                    return list;

        }

I then access the values I need by doing something like arrRand[0].tag and arrRand[0].response.

Often times I get duplicate responses from the method such as following and it becomes problematic:

[
  {
    "tag": "s22",
    "response": [
      {
        "firstName": "Name4",
        "lastName": "lastName4",
        "purchase": [
          {
            "title": "title4",
            "category": [
              "a4",
              "b4",
              "c4"
            ]
          }
        ]
      }
    ]
  },
  {
    "tag": "dsd31",
    "response": [
      {
        "firstName": "Name2",
        "lastName": "lastName2",
        "purchase": [
          {
            "title": "title2",
            "category": [
              "a2",
              "b2",
              "c2"
            ]
          }
        ]
      }
    ]
  },
  {
    "tag": "dsd31",
    "response": [
      {
        "firstName": "Name2",
        "lastName": "lastName2",
        "purchase": [
          {
            "title": "title2",
            "category": [
              "a2",
              "b2",
              "c2"
            ]
          }
        ]
      }
    ]
  }
]

My goal is to send API requests with a random "tags" value from above and then match the response I get from the call to the expectedResponse part of the data.

My initial thought was to do something like:

data.Total_packages.tags[Math.floor(Math.random() * data.Total_packages.tags.length)];

However, I can't call on "tags" without traversing through its parent i.e "package1" or "package2", so then it won't be random anymore.

I know there is probably a very simple way to do it that I am not getting. Any advice would be appreciated.

hungryhippos
  • 617
  • 4
  • 15
  • you mean you want one whole array with all tags from both "packages"? – joe Aug 30 '22 at 15:24
  • @joe No, I need one value from the "tags" tag from either packages. That tag will be run on the API call, and then which "package" that tag belonged to, I will match the response to the "expectedResponse" tag and make sure they match. – hungryhippos Aug 30 '22 at 15:28

3 Answers3

2

You could build an array from the various tags elements, and use that to do random things.

const data =
{
    "Total_packages": {
        "package1": {
            "tags": [
                "kj21",
                "j1",
                "sj2",
                "z1"
            ],
            "expectedResponse": [
                {
                    "firstName": "Name",
                    "lastName": "lastName",
                    "purchase": [
                        {
                            "title": "title",
                            "category": [
                                "a",
                                "b",
                                "c"
                            ]
                        }
                    ]
                }
            ]
        },
        "package2": {
            "tags": [
                "s2",
                "dsd3",
                "mhg",
                "sz7"
            ],
            "expectedResponse": [
                {
                    "firstName": "Name1",
                    "lastName": "lastName1",
                    "purchase": [
                        {
                            "title": "title1",
                            "category": [
                                "a1",
                                "b1",
                                "c1"
                            ]
                        }
                    ]
                }
            ]
        }
    }
}

const arrayOfTags = Object.entries(data.Total_packages).reduce((acc, [k, v]) => {
  if (v.tags) acc = acc.concat(v.tags.map(t => ({tag: t, response: v.expectedResponse})));
  return acc;
}, []);

const randomIndex = Math.floor(Math.random() * arrayOfTags.length);

const randomTag = arrayOfTags[randomIndex];

console.log(randomTag.tag, randomTag.response);
James
  • 20,957
  • 5
  • 26
  • 41
  • This worked well, however, it seems to combine both sets of "tags" and picks one randomly from them. I am sorry if I wasn't clear but I need one value from either "tags" instances and then grab its respective response. – hungryhippos Aug 31 '22 at 05:53
  • random tag from package 1 + response from package 1, random tag from package 2 + response from package 2? – James Aug 31 '22 at 12:10
  • Each API call will pick a random tag from above and then match the response from the API call to the response for that tag above. So if the tag happens to be picked from package 1, then response for package 1 will be picked. – hungryhippos Aug 31 '22 at 12:56
  • Hmm, so, because there are 2 packages, there will be 2 (random tag + response) combinations, right? And those tags can be picked from any of the tags, not just those that "go" with a specific package? So it's ok if you get [random tag + response from p1, another random tag + response from p1]? – James Aug 31 '22 at 13:02
  • No, each API call only needs one tag input. And the response from that API call will only return one response. My goal was to make sure I don't keep picking from the same package and matching its respective response. Wanted to throw some randomization in there. So each API call picks ONE tag from either package on each run and then matches its response with the actual API response. So it will be: [random tag (either p1 or p2) + response from the package that that tag belongs to]. Hope that helps and I am not confusing you. – hungryhippos Aug 31 '22 at 13:26
  • 1
    Modified. Now it includes the response for each tag in the array of tags. So you can pick one at random, check the tag property and check the response property. – James Aug 31 '22 at 14:15
  • This is awesome, thank you @James. I am facing some issues with duplicate random items but I am working with splice/shuffle to get around that. If you have any ideas, please share. You've already been more than helpful. – hungryhippos Sep 02 '22 at 15:01
  • 1
    I would use a shuffling algorithm like Fisher-Yates, see https://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array – James Sep 02 '22 at 19:27
1

You will need to mine the tags, put them together into an array and randomize the index, finally get the value there. The solution below assumes that your tags are all arrays inside data.Total_packages[whateverpackage]

const data =
{
    "Total_packages": {
        "package1": {
            "tags": [
                "kj21",
                "j1",
                "sj2",
                "z1"
            ],
            "expectedResponse": [
                {
                    "firstName": "Name",
                    "lastName": "lastName",
                    "purchase": [
                        {
                            "title": "title",
                            "category": [
                                "a",
                                "b",
                                "c"
                            ]
                        }
                    ]
                }
            ]
        },
        "package2": {
            "tags": [
                "s2",
                "dsd3",
                "mhg",
                "sz7"
            ],
            "expectedResponse": [
                {
                    "firstName": "Name1",
                    "lastName": "lastName1",
                    "purchase": [
                        {
                            "title": "title1",
                            "category": [
                                "a1",
                                "b1",
                                "c1"
                            ]
                        }
                    ]
                }
            ]
        }
    }
}

let tags = [];
for (let key in data.Total_packages) {
    if (data.Total_packages[key].tags) tags = tags.concat(data.Total_packages[key].tags);
}
console.log(tags[parseInt(tags.length * Math.random())]);

EDIT

In the comment section you have mentioned that you have duplicates of the form of

[
  {
    "tag": "s22",
    "response": [
      {
        "firstName": "Name4",
        "lastName": "lastName4",
        "purchase": [
          {
            "title": "title4",
            "category": [
              "a4",
              "b4",
              "c4"
            ]
          }
        ]
      }
    ]
  },
  {
    "tag": "dsd31",
    "response": [
      {
        "firstName": "Name2",
        "lastName": "lastName2",
        "purchase": [
          {
            "title": "title2",
            "category": [
              "a2",
              "b2",
              "c2"
            ]
          }
        ]
      }
    ]
  },
  {
    "tag": "dsd31",
    "response": [
      {
        "firstName": "Name2",
        "lastName": "lastName2",
        "purchase": [
          {
            "title": "title2",
            "category": [
              "a2",
              "b2",
              "c2"
            ]
          }
        ]
      }
    ]
  }
]

This is how you can get rid of duplicates:

let input = [
  {
    "tag": "s22",
    "response": [
      {
        "firstName": "Name4",
        "lastName": "lastName4",
        "purchase": [
          {
            "title": "title4",
            "category": [
              "a4",
              "b4",
              "c4"
            ]
          }
        ]
      }
    ]
  },
  {
    "tag": "dsd31",
    "response": [
      {
        "firstName": "Name2",
        "lastName": "lastName2",
        "purchase": [
          {
            "title": "title2",
            "category": [
              "a2",
              "b2",
              "c2"
            ]
          }
        ]
      }
    ]
  },
  {
    "tag": "dsd31",
    "response": [
      {
        "firstName": "Name2",
        "lastName": "lastName2",
        "purchase": [
          {
            "title": "title2",
            "category": [
              "a2",
              "b2",
              "c2"
            ]
          }
        ]
      }
    ]
  }
];

let output = input.filter((item, index) => {
    return input.filter((item2, index2) => {
        return ((item.tag === item2.tag) && (index2 < index));
    }).length === 0;
});

console.log(output);

We search for items who have no matches on earlier indexes.

Lajos Arpad
  • 64,414
  • 37
  • 100
  • 175
  • thank you for your suggestion. I am sorry for not being clear before, I also needed the corresponding "expectedResponse" item from each "tag" as well. Each of these random values need to be unique and not repeat. I did get responses from others that were helpful as well but I also wanted to make it clearer here. Thanks again. – hungryhippos Sep 02 '22 at 15:03
  • @hungryhippos how should the output look alike? – Lajos Arpad Sep 03 '22 at 17:04
  • I edited my post and added an expected response block. Some suggestions by others are returning a format in that way. However, the problem is, when I make multiple API calls using the method, sometimes it picks up duplicate "tags" when I have to use unique ones on each call. My idea is to add an empty array in the method and and use a for loop and inside the loop use a splice or shuffle method to make sure I get rid of any duplicates. So far my efforts have failed :( – hungryhippos Sep 05 '22 at 03:25
  • @hungryhippos I understand the nature of the problem, but I need more solid information about how this is causing the problem. Can you create a snippet where you simulate the bad behavior? – Lajos Arpad Sep 05 '22 at 17:06
  • I edited my post to add the actual code I am using and explanation. Please let me know if you need anything else. – hungryhippos Sep 09 '22 at 20:25
  • @hungryhippos how are the duplicates manifesting? Can you convert your latest code into a snippet where we can easily reproduce the problem? – Lajos Arpad Sep 09 '22 at 21:38
  • I just edited and added the snippet. My apologies for the delay, I didn't know how to actually add it until just now. – hungryhippos Sep 09 '22 at 22:31
  • @hungryhippos no worries. How is the duplicate manifesting in your snippet? – Lajos Arpad Sep 10 '22 at 11:45
  • I added how the duplicate manifests itself. – hungryhippos Sep 10 '22 at 15:18
  • 1
    @hungryhippos edited my answer, added a snippet that eliminates the duplicates from your example. – Lajos Arpad Sep 11 '22 at 10:48
  • Thank you so much @Lajos Arpad! You have already done more than enough but can you explain it in a sentence or so? I would like to understand what you did and how you did it so I can learn. What I do sort of get from it is if the tag from first item is the same as the tag from the second, then to exclude it from the return? – hungryhippos Sep 12 '22 at 01:33
  • 1
    @hungryhippos I have a nested call for `.filter()`. The outer call searches for items that do not have a match in the inner call for `.filter()`. The inner call for `.filter()` takes the "current" element and index (`item` and `index`, respectively) and checks whether there exists another element and index (`item2` and `index2`, respectively) whose tag matches the tag of `item`, but `index2 < index`. If this condition is met for any element, then the outer filter will exclude `item` from the results. – Lajos Arpad Sep 12 '22 at 12:54
  • 1
    @hungryhippos in other words, the two filters basically ensure that only the earliest tag from the array will be taken into account, therefore duplicates with higher indexes will be ignored. If my answer solved your problem, then you may consider accepting it as the correct answer. – Lajos Arpad Sep 12 '22 at 12:55
  • Thank you for the thorough explanation! Unfortunately it worked well for about six iterations and then I got a repeat again. I am not sure what's going wrong...I may be doing something wrong before it ever gets to your filter. Need to track it down. – hungryhippos Sep 12 '22 at 13:32
  • @hungryhippos it is possible that either you or I (or both) missed something. Maybe if you could ask a separate question where you would have a snippet with an input where this deduplicator fails and link that question here. It is probably something we can solve without too much trouble, but I need to know more about the issue in order to solve it. – Lajos Arpad Sep 12 '22 at 13:50
  • Good idea, I posted it here: https://stackoverflow.com/questions/73691380/deduplictor-function-is-failing-to-remove-repeats-on-random-iterations – hungryhippos Sep 12 '22 at 15:09
  • @hungryhippos I see it already has been answered. Is the answer working for you? – Lajos Arpad Sep 13 '22 at 09:59
1

i saved all tags in a separate array and kept the ref of expectedResponse in the block. then it's a matter or randomly selecting one tag index and matching with the block expectedResponse index. probably needs some adjustment to the random/length but i hope it puts you in the right direction.

var tags = []
var block = Object.values(data.Total_packages).flat()
block.forEach(item => tags = [...tags, ...item.tags])

var index = tags[parseInt(tags.length * Math.random())]
console.log(block[Math.floor(tags.indexOf(index) / 4)].expectedResponse)
joe
  • 1,359
  • 14
  • 19
  • This is great, thank you! Can you explain why in the last line, you are dividing by 4? I am getting an out of bounds error around half the time I run this. – hungryhippos Aug 31 '22 at 06:22
  • 1
    because there are 4 tags in each node, but when indexOf returns a match "position 3", it is part of the first element in `block` floor(3/4) = index 0, "position 5" floor(5/4) = index 1. it sounds like your `block` array has only one entry, hence why out of bounds if the index is greater than zero. – joe Aug 31 '22 at 08:49
  • Well done, I understand now and really appreciate your answer. One last question: if my list of total tags changes in the future, is there a way I can make the /4 part agnostic of total number of elements under tags? Like couldn't I do something like /length of tags? That way I don't have to keep going and changing the total number of tags IF they change? – hungryhippos Aug 31 '22 at 13:21
  • 1
    sure can do that. it wont work if the number of tags is not the same in every node though, otherwise i think it will do the trick for you. – joe Aug 31 '22 at 14:45