Check for duplicated objects in an array populated dynamically - javascript

I'm trying to build a data structure where all elements would be grouped based on an object key.
Everything works fine except that I can't check if the new array has the data duplicated, as it's outside the for..of loop. I'm looking for a way to prevent pushing a further object if the new array already has it.
Current output (note that the list of characters from Japan appear twice)
[
[
{ "country": "US" },
[
{ "name": "Guile", "country": "US" }
]
],
[
{ "country": "Japan" },
[
{ "name": "E. Honda", "country": "Japan" },
{ "name": "Ryu", "country": "Japan" }
]
],
[
{ "country": "Japan" },
[
{ "name": "E. Honda", "country": "Japan" },
{ "name": "Ryu", "country": "Japan" }
]
],
[
{ "country": "Thailand" },
[
{ "name": "Sagat", "country": "Thailand" }
]
]
]
Expected output
[
[
{ "country": "US" },
[
{ "name": "Guile", "country": "US" }
]
],
[
{ "country": "Japan" },
[
{ "name": "E. Honda", "country": "Japan" },
{ "name": "Ryu", "country": "Japan" }
]
],
[
{ "country": "Thailand" },
[
{ "name": "Sagat", "country": "Thailand" }
]
]
]
What I have so far
var data = [
{name: 'Guile', country: 'US'},
{name: 'E. Honda', country: 'Japan'},
{name: 'Ryu', country: 'Japan'},
{name: 'Sagat', country: 'Thailand'}
]
const getNationList = (streetFighterList) => {
let filteredList = []
for (const [index, characterData] of streetFighterList.entries()) {
// .......................................................
// looking for ways here to check if `filteredList` already
// has the data I'm trying to push. Since it's empty
// I don't know how to check its index. :(
// NOTE: indexOf() doesn't seem to work
// .......................................................
const indexOf = filteredList.indexOf(streetFighterList[index].country)
if (indexOf == -1) {
filteredList.push([
{ country: characterData.country },
streetFighterList.filter((character) => {
return character.country === characterData.country
})
])
}
}
return filteredList
}
console.log(getNationList(data))
Note: I understand that given the country object is always unique this data structure would be better and easier if I used a string instead. However this is a sample data and in a real life code I do need it stored as an object.

I would recommend using some to validate as following
var data = [
{name: 'Guile', country: 'US'},
{name: 'E. Honda', country: 'Japan'},
{name: 'Ryu', country: 'Japan'},
{name: 'Sagat', country: 'Thailand'}
]
const getNationList = (streetFighterList) => {
let filteredList = []
for (const [index, characterData] of streetFighterList.entries()) {
const entry = filteredList.some(item => item[0].country === streetFighterList[index].country)
if (!entry) {
filteredList.push([
{ country: characterData.country },
streetFighterList.filter((character) => {
return character.country === characterData.country
})
])
}
}
return filteredList
}
console.log(getNationList(data))

Reduce the array to object, with the country names as the keys. Combine players that are under the same country to an object, with the player's name as the key.
When done, convert back to an array using Object.values(), and map the array to convert the player's objects to arrays via Object.values() as well.
const data = [[{"country":"US"},[{"name":"Guile","country":"US"}]],[{"country":"Japan"},[{"name":"E. Honda","country":"Japan"},{"name":"Ryu","country":"Japan"}]],[{"country":"Japan"},[{"name":"E. Honda","country":"Japan"},{"name":"Ryu","country":"Japan"}]],[{"country":"Thailand"},[{"name":"Sagat","country":"Thailand"}]]]
const result = Object.values(data.reduce((r, [c, p]) => {
if(!r[c.country]) r[c.country] = [c, {}]
const players = r[c.country][1];
p.forEach(o => { if(!players[o.name]) players[o.name] = o; })
return r;
}, {})).map(([c, p]) => [c, Object.values(p)]);
console.log(result)

You could first create a Set of unique countries and then loop through them to combine each one with a list of fighters from that country. For example:
const data = [{ name: 'Guile', country: 'US' }, { name: 'E. Honda', country: 'Japan' }, { name: 'Ryu', country: 'Japan' }, { name: 'Sagat', country: 'Thailand' }];
const countries = new Set(data.map((obj) => obj.country));
const output = [...countries].map((country) => {
const entries = data.filter((obj) => obj.country === country);
return [{ country }, entries];
});
console.log(output);
/*
[
[
{"country": "US"},
[{"name": "Guile", "country": "US"}]
],
[
{"country": "Japan"},
[{"name": "E. Honda", "country": "Japan"}, {"name": "Ryu", "country": "Japan" }]
],
[
{"country": "Thailand"},
[{"name": "Sagat", "country": "Thailand"}]
]
]
*/

Related

Fuse.js - search through nested values

I use fuse.js with useExtendedSearch enabled. I have an array of objects. Each of the objects has a key of type array that contains objects.
I want to search for values in both the outer array and the inner array. It works if the search keyword is in the outer array. But it does not work, if the inner array contains the search keyword.
In the code below, I want to filter the countries such that it contains only the object whose name is exactly as the search keyword.
const data = [{
"continent": "Asia",
countries: [{
name: "Korea"
},
{
name: "Japan"
},
{
name: "China"
},
{
name: "Pakistan"
},
]
},
{
"continent": "Europe",
countries: [{
name: "Albania"
},
{
name: "France"
},
{
name: "England"
},
{
name: "Spain"
},
]
},
{
"continent": "Africa",
countries: [{
name: "Algeria"
},
{
name: "Angola"
},
{
name: "Benin"
},
{
name: "South Africa"
},
]
}
]
const options = {
useExtendedSearch: true,
keys: [
"continent",
"countries.name"
]
};
const fuse = new Fuse(data, options);
// Change the pattern
const pattern = "=Pakistan"
console.log(fuse.search(pattern))
<script src="https://cdn.jsdelivr.net/npm/fuse.js#6.6.2"></script>
But it only filters the outer array.
[{
"continent": "Asia",
countries: [{
name: "Korea"
},
{
name: "Japan"
},
{
name: "China"
},
{
name: "Pakistan"
},
]
}]
I expect it to retun:
[{
"continent": "Asia",
countries: [{
name: "Pakistan"
}]
}]
Is it possible to achive this in Fuse.js?

Building array from object of arrays returns undefined

So I have this originalArray:
[
[
{
“name”: “Selena”,
“lastName”: “Gomez”,
“likes”: {
“color”: “red”,
“country”: “Argentina”,
“state”: {
“name”: “Buenos Aires”
}
},
“phoneNumber”: “5555555555”,
“isAvailable”: false
}
],
[
{
“name”: “Nick”,
“lastName”: “Jonnas”,
“likes”: {
“color”: “blue”,
“country”: “Argentina”,
“state”: {
“name”: “Buenos Aires”
}
},
“phoneNumber”: “7777777777”,
“isAvailable”: true
},
{
“name”: “Joe”,
“lastName”: “Jonnas”,
“likes”: {
“color”: “yellow”,
“country”: “Argentina”,
“state”: {
“name”: “Buenos Aires”
}
},
“phoneNumber”: “9999999999”,
“isAvailable”: false
}
]
]
As you can see is an Object that has the form of (2) [Array(1), Array(2)] and I want to iterate over the object and get only the phoneNumbers items.
Expected output:
['55555555555', '7777777777', '9999999999']
I've tried:
const newArray= originalArray.map(element => element.phoneNumber);
But it returns undefined, actually I tried accessing with . like originalArray.phoneNumber and shows undefined too.
I also tried with this approach with no result:
var newArray = originalArray.filter(obj => {
return obj.phoneNumber != null
})
Is there a way to iterate over the object, look up the phoneNumber key, and add those results to a new array?
const phoneNumbers = originalArray.flat().map(element => element.phoneNumber);
const originalArray = [
[
{
name: "Selena",
lastName: "Gomez",
likes: {
color: "red",
country: "Argentina",
state: {
name: "Buenos Aires",
},
},
phoneNumber: "5555555555",
isAvailable: false,
},
],
[
{
name: "Nick",
lastName: "Jonnas",
likes: {
color: "blue",
country: "Argentina",
state: {
name: "Buenos Aires",
},
},
phoneNumber: "7777777777",
isAvailable: true,
},
{
name: "Joe",
lastName: "Jonnas",
likes: {
color: "yellow",
country: "Argentina",
state: {
name: "Buenos Aires",
},
},
phoneNumber: "9999999999",
isAvailable: false,
},
],
];
const phoneNums = originalArray.flat().map((element) => element.phoneNumber);
console.log(phoneNums);
You were close, just needed to add map. Try this
var newArray = originalArray.flat()
.filter(obj => obj.phoneNumber!=null)
.map(obj => { return obj.phoneNumber;});
We can achieve this by :
Flattening the multidimensional array into single array.
looping through each object using Array.map() to fetch the phone number.
This step is for safe side to eliminate the empty or null phone numbers using Array.filter()
Working Demo :
let originalArray = [
[
{
"name": "Selena",
"lastName": "Gomez",
"likes": {
"color": "red",
"country": "Argentina",
"state": {
"name": "Buenos Aires"
}
},
"phoneNumber": "5555555555",
"isAvailable": false
}
],
[
{
"name": "Nick",
"lastName": "Jonnas",
"likes": {
"color": "blue",
"country": "Argentina",
"state": {
"name": "Buenos Aires"
}
},
"phoneNumber": "7777777777",
"isAvailable": true
},
{
"name": "Joe",
"lastName": "Jonnas",
"likes": {
"color": "yellow",
"country": "Argentina",
"state": {
"name": "Buenos Aires"
}
},
"phoneNumber": "9999999999",
"isAvailable": false
}
]
];
const flatArray = originalArray.flat().map((obj) => obj.phoneNumber).filter((item) => item);
console.log(flatArray);

Nested filter returning empty array

Below is the data that I am receiving and I am trying to filter so that a new array contains only objects with the desired location.
However, I'm running into an issue where my function is returning [], an empty array.
data:
[
{ data: [[Object], [Object], [Object]], id: 1 },
{ data: [[Object]], id: 2 },
{ data: [[Object], [Object], [Object], [Object]], id: 3 }
];
data[1]:
{"data": [{"name": "Joe", "job": "N/A", "location": "Los Angeles"}], "id": 2}
This is my current function:
const locations = ["Los Angeles", "Chicago"];
...
const filteredData = data.filter((i) =>
i.data.filter((j) => locations.includes(j.location)),
);
return filteredData;
What is wrong and how can I fix this and get it filtering correctly?
In the callback you pass to the Array.filter(), you need to return a boolean value to filter the array. If you do not return anything, the filter returns an empty array.
But in your case, you are returning inner filtered array that returns at least an empty array and the outer filter behaves it as a true value. So the outer filter will return all of the items in the original array. (not an empty one as you stated)
Also you are returning filteredData in a place where it results in a syntax error.
const data = [
{"data": [{"name": "Joe", "job": "N/A", "location": "Los Angeles"}], "id": 2},
{"data": [{"name": "Jane", "job": "N/A", "location": "Charlotte"}], "id": 3},
]
const locations = ["Los Angeles", "Chicago"];
const filteredData = data.filter((i) =>
i.data.filter((j) => locations.includes(j.location)).length > 0,
);
console.log(filteredData);
Another Option is use some() to get your expected result. This way you don't need to loop through all item in data array comparing to filter()
const data = [
{ data: [{ name: "Joe", job: "N/A", location: "Los Angeles" }], id: 2 },
{ data: [{ name: "Jane", job: "N/A", location: "Charlotte" }], id: 3 },
{ data: [{ name: "Sam", job: "N/A", location: "SSS" }], id: 4 },
{
data: [
{ name: "John", job: "N/A", location: "AAA" },
{ name: "Doe", job: "N/A", location: "BBB" },
],
id: 5,
},
];
const locations = ["Los Angeles", "Chicago", "AAA"];
const existData = data.filter(el =>
el.data.some(item => locations.includes(item.location))
);
console.log(existData);
If you also want to filter the data array, you can do like below.
const data = [
{ data: [{ name: "Joe", job: "N/A", location: "Los Angeles" }], id: 2 },
{ data: [{ name: "Jane", job: "N/A", location: "Charlotte" }], id: 3 },
{ data: [{ name: "Sam", job: "N/A", location: "SSS" }], id: 4 },
{
data: [
{ name: "John", job: "N/A", location: "AAA" },
{ name: "Doe", job: "N/A", location: "BBB" },
],
id: 5,
},
];
const locations = ["Los Angeles", "Chicago", "AAA"];
const filteredData = data.reduce((acc, cur) => {
const filteredItem = cur.data.filter(item => locations.includes(item.location));
if (filteredItem.length) {
acc.push({ ...cur, data: filteredItem });
}
return acc;
}, []);
console.log(filteredData);

nesting array of object in javascript

I want to simplify nested array of object to array of string without altering original array, but value of countries array of object get changed. How I will rectify so I'll get new array of object in newCountries and old value of array of object not get modified. i/e countries
const countries = {
"country": [{
"name": "India",
"cities": [{
"name": "Mumbai",
},
{
"name": "Delhi",
}
]
},
{
"name": "Australia",
"cities": [{
"name": "Sydney",
},
{
"name": "Melbourne",
}
]
}
]
}
// I have tried following code
var newCountries = Object.assign({}, countries);
newCountries.country.map(val => {
if (val.cities) {
var city = val.cities.map(city => {
return city.name
});
val.cities = [];
val.cities = city;
}
return val;
})
console.log('new object\n', newCountries)
console.log('old object\n', countries)
<b>output</b>
<pre>
{
"country": [
{
"name": "India",
"cities": [
"Mumbai",
"Delhi"
]
},
{
"name": "Australia",
"cities": [
"Sydney",
"Melbourne"
]
}
]
}
</pre>
Well, this is a trivial task. Just loop over each country and assign a new array of city names to the existing one by mapping the object to just the value of the "name" key.
You could also try this, assuming name is the only key in the object.
country.cities = country.cities.reduce((arr, city) => arr.concat(Object.values(city)), [])
You can even simplify this further by using Array.prototype.flatMap:
country.cities = country.cities.flatMap(city => Object.values(city))
Note: flatMap is somewhat of an experimental feature in modern browsers.
Chrome – Version 69 (2018-09-04)
Edge – No
Firefox – Version 62 (2019-09-03)
Internet Explorer – No
Opera – Version 56 (2018-09-25 → Chromium 69)
Safari – Version 12 (2018-09-17)
const countries = {
"country": [{
"name": "India",
"cities": [{
"name": "Mumbai",
}, {
"name": "Delhi",
}]
}, {
"name": "Australia",
"cities": [{
"name": "Sydney",
}, {
"name": "Melbourne",
}]
}]
}
// Loop over each country
countries.country.forEach(country => {
// Replace `{ name : "CITY_NAME" }` with just `"CITY_NAME"`
country.cities = country.cities.map(city => city.name)
})
console.log(countries);
.as-console-wrapper { top: 0; max-height: 100% !important; }

How to check an array inside json data is empty using jmespath?

I have json data as below:
[
{
"id": "i_1",
"name": "abc",
"address": [
{
"city": [
"city1",
"city2"
]
},
{
"city": [
"city1",
"city2"
]
}
]
},
{
"id": "i_2",
"name": "def",
"address": [
{
"city": []
},
{
"city": []
}
]
}
]
Now, I want only that data where city array is not null. So in the above example the output should be 1st element i.e. with id i_1.
How to filter this json using jmespath library?
You can do this:
var arr = [
{
"id": "i_1",
"name": "abc",
"address": [
{
"city": [
"city1",
"city2"
]
},
{
"city": [
"city1",
"city2"
]
}
]
},
{
"id": "i_2",
"name": "def",
"address": [
{
"city": []
},
{
"city": []
}
]
}
];
console.log(jmespath.search(arr,"[?not_null(address[].city[])]"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/jmespath/0.15.0/jmespath.js"></script>
You could do this in pure javascript using filter and every
const items=[{"id":"i_1","name":"abc","address":[{"city":["city1","city2"]},{"city":["city1","city2"]}]},{"id":"i_2","name":"def","address":[{"city":[]},{"city":[]}]}]
const filtered = items.filter(i => i.address.every(a => a.city && a.city.length > 0))
console.log(filtered)
This returns only if every object inside address has a non-empty city array.
var arr = [
{
"id": "i_1",
"name": "abc",
"address": [
{
"city": [
"city1",
"city2"
]
},
{
"city": [
"city1",
"city2"
]
}
]
},
{
"id": "i_2",
"name": "def",
"address": [
{
"city": []
},
{
"city": []
}
]
}
];
console.log(jmespath.search({ c: arr}, "not_null(c[].address[].city[])"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/jmespath/0.15.0/jmespath.js"></script>
I don't know what it's your result. can you explain better?
You don't need to use jmespath library use filter and `every from the vanilla JS. It is more efficient.
let jsonTxt = '{"data":[{"id":"i_1","name":"abc","address":[{"city":["city1","city2"]},{"city":["city1","city2"]}]},{"id":"i_2","name":"def","address":[{"city":[]},{"city":[]}]}]}'
let jsonData = JSON.parse(jsonTxt);
let items = jsonData.data;
const result = items.filter(i => i.address.every(a => a.city && a.city.length))
console.log('id: ', result[0].id);
//using jmespath
console.log(jmespath.search({data: items}, "data[*].address[*].city"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/jmespath/0.15.0/jmespath.js"></script>

Categories