Fuse.js - search through nested values - javascript

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?

Related

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);

Return name and id property value of all arrays inside object

How to return name and id property value of all arrays? The idea is to make a single map of all these arrays and return the id and name?
Something like this
filters.[key].map((option, index) => (
<ItemFilter key={index}>{option}</ItemFilter>
))
I have this array object
filters: {
"services": [
{
"id": "1b975589-7111-46a4-b433-d0e3c0d7c08c",
"name": "Bank"
},
{
"id": "91d4637e-a17f-4b31-8675-c041fe06e2ad",
"name": "Income"
}
],
"accountTypes": [
{
"id": "1f34205b-2e5a-430e-982c-5673cbdb3a68",
"name": "Digital Account"
}
],
"channels": [
{
"id": "875f8350-073e-4a20-be20-38482a86892b",
"name": "Chat"
}
]
}
You can use flatMap or flat to achieve the desired result.
Object.values(obj.filters).flatMap(v => v)
or
Object.values(obj.filters).flat()
const obj = {
filters: {
services: [
{
id: "1b975589-7111-46a4-b433-d0e3c0d7c08c",
name: "Bank",
},
{
id: "91d4637e-a17f-4b31-8675-c041fe06e2ad",
name: "Income",
},
],
accountTypes: [
{
id: "1f34205b-2e5a-430e-982c-5673cbdb3a68",
name: "Digital Account",
},
],
channels: [
{
id: "875f8350-073e-4a20-be20-38482a86892b",
name: "Chat",
},
],
},
};
const result = Object.values(obj.filters).flatMap(v => v);
console.log(result);
If option is referring to name in your example code it could look something like this:
Object.values(
{
filters: {
services: [
{
id: "1b975589-7111-46a4-b433-d0e3c0d7c08c",
name: "Bank",
},
{
id: "91d4637e-a17f-4b31-8675-c041fe06e2ad",
name: "Income",
},
],
accountTypes: [
{
id: "1f34205b-2e5a-430e-982c-5673cbdb3a68",
name: "Digital Account",
},
],
channels: [
{
id: "875f8350-073e-4a20-be20-38482a86892b",
name: "Chat",
},
],
},
}.filters
)
.flat()
.map(({ name, index }) => <ItemFilter key={index}>{name}</ItemFilter>);

Move two items of array to front in javascript

I have an array address like so
[
{
id: "83",
country: "China",
type: "NORMAL"
},
{
id: "84",
country: "California",
type: "HOME"
},
{
id: "85",
country: "Brazil",
type: "NORMAL"
},
{
id: "86",
country: "India",
type: "WORK"
},
]
How I move two items with type is HOME and WORK to the front of array like so: HOME first then come WORK then another.
[
{
id: "84",
country: "California",
type: "HOME"
},
{
id: "86",
country: "India",
type: "WORK"
},
{
id: "83",
country: "China",
type: "NORMAL"
},
{
id: "85",
country: "Brazil",
type: "NORMAL"
},
]
Thank you :))
I think you can use JS array sort method, I provide you an example below (pseudo code):
const order = ['HOME', 'WORK', 'NORMAL'];
const arr = [{id: "83", country: "China", type: "NORMAL"}, {...}] // your array to sort
const sorted = arr.sort((a, b) => order.indexOf(a.type) > order.indexOf(b.type));

Check for duplicated objects in an array populated dynamically

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"}]
]
]
*/

add value to inner array using map in javascript

Hello suppose I have the following array:
let array = [
{
id: "1",
name: "name",
categories: [
{
subid: "10",
name: "name",
},
{
subid: "11",
name: "name",
}
]
},
{
id: "2",
name: "name",
categories: [
{
subid: "20",
name: "name",
},
{
subid: "21",
name: "name",
}
]
}
]
My goal is to take the id of each of the objects and add it to the inner array categories. So it would look like this:
let array = [
{
id: "1",
name: "name",
categories: [
{
subid: "10",
name: "name",
id: "1"
},
{
subid: "11",
name: "name",
id: "1"
}
]
},
{
id: "2",
name: "name",
categories: [
{
subid: "20",
name: "name",
id: "2"
},
{
subid: "21",
name: "name",
id: "2"
}
]
}
]
Here is what I have so far:
array.map(x => (x.id)) // returns new array of ids
// add these into the categories
Can someone help me figure this out using map? If map cant be used I think for each will work as well
With map method and spread syntax inside object you could do this.
let array = [{"id":"1","name":"name","categories":[{"subid":"10","name":"name"},{"subid":"11","name":"name"}]},{"id":"2","name":"name","categories":[{"subid":"20","name":"name"},{"subid":"21","name":"name"}]}]
let result = array.map(({id, categories, ...rest}) => ({
...rest, id, categories: categories.map((o) => ({...o, id}))
}))
console.log(result)
You can use Array.forEach() to iterate over the array and then use Array.map() on categories array to add the id prop to all its objects:
let array = [ { id: "1", name: "name", categories: [ { subid: "10", name: "name", }, { subid: "11", name: "name", } ] }, { id: "2", name: "name", categories: [ { subid: "20", name: "name", }, { subid: "21", name: "name", } ] } ];
array.forEach((o)=>{
o.categories = o.categories.map(cat=>Object.assign({},cat,{id : o.id}));
});
console.log(array);
What about nested map?
let arr = [
{
id: "1",
name: "name",
categories: [
{
subid: "10",
name: "name",
},
{
subid: "11",
name: "name",
}
]
},
{
id: "2",
name: "name",
categories: [
{
subid: "20",
name: "name",
},
{
subid: "21",
name: "name",
}
]
}
]
arr.map(x=>{
x.categories.map(y => {
y.id = x.id
})
})
console.log(arr)
You need to map all arrays with copied properties to get a new independent data with a new property.
let array = [{ id: "1", name: "name", categories: [{ subid: "10", name: "name", }, { subid: "11", name: "name", }] }, { id: "2", name: "name", categories: [{ subid: "20", name: "name", }, { subid: "21", name: "name", }] }],
updated = array.map(o => Object.assign(
{},
o,
{ categories: o.categories.map(p => Object.assign({}, p, { id: o.id })) }
));
console.log(updated);
console.log(array);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Using each of Jquery should do the job.
$.each(array,function(index,item){item.categories.id = item.id;});
Thanks.

Categories