I am trying to filter a Javascript array of objects with nested objects with specific properties. I can filter the name, slug, website, launch year without any issues. But, I can not filter the category name (category.name) which is an object within the object. Why is filtering the category name not working?
var search = "qui"; // does not work (category.name)
// var search = "Sauer"; // works (name)
var data = [{ "name": "Sauer-Metz", "slug": "ab-laborum",
"website": "https://test.com", "launch_year": 2017, "category_id": 6,
"category": { "id": 6, "name": "qui", "slug": "qui" } } ];
var results = data.filter(company => [
'name', 'launch_year', 'website', 'category.name'
].some(key => String(company[key]).toLowerCase().includes(search.toLowerCase())));
console.log(results);
One way you can go about it is to have a value extractor like the one getKey below
const getKey = (value, key) => {
return key.split('.').reduce((acc, curr) => value[curr], '');
}
var results = data.filter(company => [
'name', 'launch_year', 'website', 'category.name'
].some(key => String(getKey(company, key)).toLowerCase().includes(search.toLowerCase())));
I believe you have to do a separate condition for this specific nested property, although there might be a cleaner way I don't see right now:
var results = data.filter(
(company) =>
["name", "launch_year", "website"].some((key) =>
String(company[key]).toLowerCase().includes(search.toLowerCase())
) ||
String(company["category"]["name"])
.toLowerCase()
.includes(search.toLowerCase())
);
Dot notation doesn't work like that.
const testCase1 = 'qui';
const testCase2 = 'Sauer';
const data = [
{
name: 'Sauer-Metz',
slug: 'ab-laborum',
website: 'https://test.com',
launch_year: 2017,
category_id: 6,
category: { id: 6, name: 'qui', slug: 'qui' },
},
];
const searchResults = (data, search) => {
return data.filter((item) => {
return (
item?.category?.name.toLowerCase().includes(search.toLowerCase()) ||
['name', 'launch_year', 'website'].some((key) => `${item[key]}`.toLowerCase().includes(search.toLowerCase()))
);
});
};
console.log('**CASE 1**')
console.log(searchResults(data, testCase1));
console.log('**CASE 2**')
console.log(searchResults(data, testCase2));
To use your approach you can convert 'category.name' to ['category','name'] and then use String(company[key[0]][key[1]])... whenever key is an array.
const search = "qui"; // does not work (category.name)
//const search = "Sauer"; // works (name)
const data = [{ "name": "Sauer-Metz", "slug": "ab-laborum", "website": "https://test.com", "launch_year": 2017, "category_id": 6, "category": { "id": 6, "name": "qui", "slug": "qui" } } ];
const results = data.filter(
company => [
'name', 'launch_year', 'website', ['category','name']
].some(
key =>
Array.isArray(key) ?
String(company[key[0]][key[1]]).toLowerCase().includes(search.toLowerCase()) :
String(company[key]).toLowerCase().includes(search.toLowerCase())
)
);
console.log(results);
I have the json like follow example, i would like to convert this json map to array for to be able to loop on it.
I use the Object.keys method but I don't know how to have a key => value format for the whole json.
I need to get the keys to make a table I know there is the pipe keyvalue but it's not what I need. or maybe I use it wrong
example json
{
"pays": "UK"
"test": [
[
"123456", // here i want key id
"blabla", // here i want key name
"lorem ipsum" // here i want key type
],
[
"654321",
"ipsum",
"blabla"
]
]
}
components.ts
get() {
this.myService.getUrl().subscribe(data => {
this.myArray = Object.keys(data).map((key) => {
return {
id: key,
name: data[key]
}
}):
}
Please try this
var input: any = {
"pays": "UK",
"test": [
[
"123456", // here i want key id
"blabla", // here i want key name
"lorem ipsum" // here i want key type
],
[
"654321",
"ipsum",
"blabla"
]
]
}
input .test = input.test.map((item: any) => {
return {
id: item[0],
name : item[1],
type : item[2]
}
})
This is one possible solution to transform array of strings into array of JSONs:
let input = {
pays: "UK",
test: [
[
"123456",
"blabla",
"lorem ipsum"
],
[
"654321",
"ipsum",
"blabla"
]
]
};
let result = {};
result.pays = input.pays;
result.test = [];
for (let i = 0; i < input.test.length; i++){
let testEl = input.test[i];
let resultObj = {};
resultObj.id = testEl[0];
resultObj.name = testEl[1];
resultObj.type = testEl[2];
result.test.push(resultObj);
}
console.log(result)
Having an array of objects like this:
[{"event_id":1,"person":"John"},
{"event_id":2,"person":"John"},
{"event_id":3,"person":"Mike"},
{"event_id":1,"person":"Mike"},
{"event_id":1,"person":"Anna"},
{"event_id":3,"person":"Anna"}]
the wanted result should combine them based on the event_id and show them in a table structure like this:
1 John, Mike, Ana
2 John
3 Mike, Anna
Each row represents an event and the rows contains the people who participated in that event.
I don't know how do to this in JavaScript. Any suggestions?
You can use reduce:
const data = [
{ event_id: 1, person: 'John' },
{ event_id: 2, person: 'John' },
{ event_id: 3, person: 'Mike' },
{ event_id: 1, person: 'Mike' },
{ event_id: 1, person: 'Anna' },
{ event_id: 3, person: 'Anna' },
];
const result = data.reduce(
(acc, val) => ({
...acc,
[val.event_id]: acc[val.event_id] ? [...acc[val.event_id], val.person] : [val.person],
}),
{},
);
console.log(result);
You can use a Map with .reduce() to first group your objects, where the event_id is the key in the map, and the value is an array of accumulated person values for the same event_id values. You can then use Array.from() to map each [key, value] entry to a string of HTML for the table rows and data. You can then add this string to your HTML like so:
const arr = [{"event_id":1,"person":"John"},
{"event_id":2,"person":"John"},
{"event_id":3,"person":"Mike"},
{"event_id":1,"person":"Mike"},
{"event_id":1,"person":"Anna"},
{"event_id":3,"person":"Anna"}];
const table = `
<table>
${Array.from(
arr.reduce((m, {event_id:id, person}) => m.set(id, [...(m.get(id) || []), person]), new Map),
([key, arr]) => `<tr><td>${key}</td><td>${arr.join(', ')}</td></tr>`
).join('')}
</table>
`;
document.body.innerHTML = table;
I recommend you to use Map data type.
Map is a collection of keyed data items, just like an Object. But the
main difference is that Map allows keys of any type.
First of all we iterate on Array of Objects, then we check if there is any event_id in Map we push Object.person to the value of event_id entry :
const listOfObjects = [
{ "event_id": 1, "person": "John" },
{ "event_id": 2, "person": "John" },
{ "event_id": 3, "person": "Mike" },
{ "event_id": 1, "person": "Mike" },
{ "event_id": 1, "person": "Anna" },
{ "event_id": 3, "person": "Anna" }];
let eventIdCollection = new Map();
listOfObjects.forEach(obj => {
if (eventIdCollection.has(obj.event_id)) {
let persons = eventIdCollection.get(obj.event_id);
persons.push(obj.person);
eventIdCollection.set(obj.event_id, persons);
}
else {
eventIdCollection.set(obj.event_id, [obj.person]);
}
});
the result is Map of event_id to Array of persons.
I have some data from a collection named "service" which has data like this:
let service_data = [
{
"name":"Service 1",
"price":60,
"resource_group_ids" :
["5d5e5dea99d9b75ff2f78dcd","5d5e85d329782914332368c8"]
},
{
"name":"Service 2",
"price":60,
"resource_group_ids" : ["5d5e5dea99d9b75ff2f7cfe"]
}
]
I want to push the resource_group_ids in a variable let say resource_groups. I don't want to loop resource_group_ids inside service_data.
You could use flatMap
const resource_groups = service_data.flatMap(o => o.resource_group_ids)
const service_data = [{"name":"Service 1","price":60,"resource_group_ids":["5d5e5dea99d9b75ff2f78dcd","5d5e85d329782914332368c8"]},{"name":"Service 2","price":60,"resource_group_ids":["5d5e5dea99d9b75ff2f7cfe"]}]
const resource_groups = service_data.flatMap(o => o.resource_group_ids)
console.log(resource_groups)
If flatMap is not supported, use concat and spread syntax to merge the 2D array returned by map
const resource_groups = [].concat(...service_data.map(o=> o.resource_group_ids))
You can use flatMap
const extractFlat = (arr, key) => {
return arr.flatMap(e => e[key]);
};
let service_data = [
{
"name":"Service 1",
"price":60,
"resource_group_ids" :
["5d5e5dea99d9b75ff2f78dcd","5d5e85d329782914332368c8"]
},
{
"name":"Service 2",
"price":60,
"resource_group_ids" : ["5d5e5dea99d9b75ff2f7cfe"]
}
]
const resource_group_ids = extractFlat(service_data, 'resource_group_ids');
console.log(resource_group_ids);
or reduce on older environments
const extractFlat = (arr, key) => {
return arr.reduce((acc, x) => acc.concat(x[key]), []);
};
You can achieve this with .map(), .join() and .split()
const service_data = [
{
name: 'Service 1',
price: 60,
resource_group_ids:
['5d5e5dea99d9b75ff2f78dcd', '5d5e85d329782914332368c8']
},
{
name: 'Service 2',
price: 60,
resource_group_ids: ['5d5e5dea99d9b75ff2f7cfe']
},
];
const result = service_data.map((r) => { return r.resource_group_ids;
}).join(',').split(',');
console.log(result);
Using .reduce:
let service_data = [{
"name": "Service 1",
"price": 60,
"resource_group_ids": ["5d5e5dea99d9b75ff2f78dcd", "5d5e85d329782914332368c8"]
},
{
"name": "Service 2",
"price": 60,
"resource_group_ids": ["5d5e5dea99d9b75ff2f7cfe"]
}
]
const resource_groups = service_data.reduce(function(result, item) {
result.concat(item.resource_group_ids)
}, [])
console.log(resource_groups)
I have two arrays
array1 = [{id:"1",title:"Writing"},{id:"2",title:"Singing"},{id:"3",title:"Dance"}];
array2 = [{tags: "1",title: "USA",type: "text"},
{tags: "1,2,3",title: "Japan",type: "image"},
{tags: "2,3",title: "Japan",type: "image"}];
I have to map the id of the array1 to tags of the array2 and display the corresponding title from the array1.
The new array2 should look like,
array2=[{tags:"Writing",title:"USA", type:"text"},
{tags: "Writing,Singing,Dance",title: "Japan",type: "image"},
{tags: "Singing,Dance",title: "Japan",type: "image"}];
I did this to get the array1 mapping and got stuck after that.
var newtags= (array1).map(obj=>{
var rObj={};
rObj[obj.id]=obj.title;
return rObj;
});
You can create a mapping object with each id as key and title as value using reduce. Then map over array2 and split each tags to get the new tags
const array1=[{id:"1",title:"Writing"},{id:"2",title:"Singing"},{id:"3",title:"Dance"}],
array2=[{tags:"1",title:"USA",type:"text"},{tags:"1,2,3",title:"Japan",type:"image"},{tags:"2,3",title:"Japan",type:"image"}]
const map = array1.reduce((r, { id, title }) => ({ ...r, [id]: title }), {});
const output = array2.map(({ tags, ...rest }) => {
const newTags = tags.split(',').map(id => map[id]).join(',')
return { tags: newTags, ...rest }
})
console.log(output)
You could also get the mapping object using Object.fromEntries()
const map = Object.fromEntries(array1.map(({ id, title }) => [id, title]));
Then use the regex /\d+(?=,|$)/ to match the numbers and replace them with their respective titles
const array1=[{id:"1",title:"Writing"},{id:"2",title:"Singing"},{id:"3",title:"Dance"}],
array2=[{tags:"1",title:"USA",type:"text"},{tags:"1,2,3",title:"Japan",type:"image"},{tags:"2,3",title:"Japan",type:"image"}]
const map = Object.fromEntries(array1.map(({ id, title }) => [id, title]));
const output = array2.map(({ tags, ...rest }) => {
const newTags = tags.replace(/\d+(?=,|$)/g, n => map[n])
return { tags: newTags, ...rest }
})
console.log(output)
Here's a solution
I'm using .map, .reduce and .replace to join array1 and array2 together.
const array1 = [
{
id: "1",
title: "Writing"
},
{
id: "2",
title: "Singing"
},
{
id: "3",
title: "Dance"
}
]
const array2 = [
{
tags: "1",
title: "USA",
type: "text"
},
{
tags: "1,2,3",
title: "Japan",
type: "image"
},
{
tags: "2,3",
title: "Japan",
type: "image"
}
]
const array3 =
array2.map(item => ({
...item,
tags: array1.reduce((tags, {id, title}) => tags.replace(id, title), item.tags),
}))
console.log(array3)
You can use filter, map and join method, split tags and filter tags in array1 first.
var newtags= (array2).map(obj=>{
let tags = obj.tags.split(",");
let titles = array1.filter(c=>tags.includes(c.id)).map(c=>c.title);
obj.tags = titles.join();
return obj;
});
array1 = [{id:"1",title:"Writing"},{id:"2",title:"Singing"},{id:"3",title:"Dance"}];
array2 = [{tags: "1",title: "USA",type: "text"},
{tags: "1,2,3",title: "Japan",type: "image"},
{tags: "2,3",title: "Japan",type: "image"}];
var newtags= (array2).map(obj=>{
let tags = obj.tags.split(",");
let titles = array1.filter(c=>tags.includes(c.id)).map(c=>c.title);
obj.tags = titles.join();
return obj;
});
console.log(newtags);
You can try following
Use Array.reduce to convert array1 into an object with id as key and title as value (Step 1)
Iterate over array2 using Array.forEach to update its tags property
To update tags property first split it by , to convert into an array
Map each value in array to its corresponding value in Object created in step 1
Join back the array with , and assign back to tags
let array1 = [{id:"1",title:"Writing"},{id:"2",title:"Singing"},{id:"3",title:"Dance"}];
let array2 = [{tags: "1",title: "USA",type: "text"},{tags: "1,2,3",title: "Japan",type: "image"},{tags: "2,3",title: "Japan",type: "image"}];
let obj = array1.reduce((a,c) => Object.assign(a, {[c.id] : c.title}), {});
array2.forEach(o => o.tags = o.tags.split(",").map(v => obj[v]).join(","));
console.log(array2);
To achieve expected result, use below option of looping array1 and replacing array2 tags with title
Loop Array1 using forEach
Replace array2 tags with each array1 title using array id
array1 = [{id:"1",title:"Writing"},{id:"2",title:"Singing"},{id:"3",title:"Dance"}];
array2 = [{tags: "1",title: "USA",type: "text"},
{tags: "1,2,3",title: "Japan",type: "image"},
{tags: "2,3",title: "Japan",type: "image"}];
array1.forEach(v =>{
const re = new RegExp(v.id, "g");
array2 = JSON.parse(JSON.stringify(array2).replace(re, v.title))
})
console.log(array2);
I would consider breaking this down into several reusable functions. Of course it might be premature abstraction, but I've seen variants of this questions like often enough here that it makes sense to me to look toward the fundamentals.
We want to be able to look up the values in a list stored as an array with what might be arbitrary field names. So we use a function makeDictionary that takes both the field names and the array and returns an object that maps them, such as {'1': 'Writing', '2': 'Singing',...}`.
Then we can use fillField supplying a dictionary, a field name, and an object, and replace that field with the result of looking up the tags in the dictionary. This is a little more specific to the problem, mostly because the comma-separated string format for your tags is a little more cumbersome than it might be if it were an array.
With these, useTags is simple to write, and it is the first function here focused directly on your needs. It combines the above, supplying the field names id and title for the dictionary and tags for your main objects.
This is what it looks like combined:
const makeDictionary = (keyName, valName) => (arr) =>
arr .reduce
( (a, {[keyName]: k, [valName]: v}) => ({...a, [k]: v})
, {}
)
const fillField = (field, dict) => ({[field]: f, ...rest}) => ({
...rest,
[field]: f .split (/,\s*/) .map (t => dict[t]) .join (', ')
})
const useTags = (tags, dict = makeDictionary ('id', 'title') (tags) ) =>
(objs) => objs .map ( fillField ('tags', dict) )
const tags = [{id: "1", title: "Writing"}, {id: "2", title: "Singing"}, {id: "3", title: "Dance"}];
const updateTags = useTags (tags)
const items = [{tags: "1", title: "USA", type: "text"}, {tags: "1, 2, 3", title: "Japan", type: "image"}, {tags: "2, 3", title: "Japan", type: "image"}];
console .log (
updateTags (items)
)
Note that I took a little liberty with the tags: "2,3" and tags: "Singing,Dance" formats, adding a little white space. It's trivial to take this out. But even better, if possible, would be to change this to use arrays for your tags.
You could take a real Map and map the values to the new objects.
var array1 = [{ id: "1", title: "Writing" }, { id: "2", title: "Singing" }, { id: "3", title: "Dance" }],
array2 = [{ tags: "1", title: "USA", type: "text" }, { tags: "1,2,3", title: "Japan", type: "image" }, { tags: "2,3", title: "Japan", type: "image" }],
tags = array1.reduce((m, { id, title }) => m.set(id, title), new Map),
result = array2.map(o => ({ ...o, tags: o.tags.split(',').map(Map.prototype.get, tags).join() }));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }