Given a JavaScript object which represents a JSON like so -
[
{
"Id": "8868dfdd-9b4e-4bad-a4ce-ecae6a3cc828",
"Name": "Company 1",
"Locations": [
{
"Id": "bd017b9c-b62e-43aa-9f00-c164a855eed1",
"Name": "Location 1",
"Departments": [
{
"Id": "c9e4afe3-bbdb-474f-9062-2935025bfa2e",
"Name": "Department 1",
"Employees": [
{
"Id": "92c3a085-5712-422d-8b0f-922b57889c4f",
"Name": "Employee 1",
"Title": "FrontEnd Engineer",
"Location": "New York",
"Photo": ""
}
]
}
]
}
]
}
]
I want to filter this data structure by employee name, given that there might be multiple company, location, department. Here is my attempt at it but clearly it is not working due to my understanding of how Array.filter or Array.reduce works.
filterContacts = search => {
if (search.trim() === "") {
this.setState({ filteredContacts: null, search: search });
} else {
let filteredArray = this.state.contacts.reduce((f, c) => {
let clone = [];
for (let i = 0; i < c.Locations.length; i++) {
const l = c.Locations[i];
for (let j = 0; j < l.Departments.length; j++) {
const d = l.Departments[j];
for (let k = 0; k < d.Employees.length; k++) {
const e = d.Employees[k];
if (e.Name.search(new RegExp(search, "i") > -1)) {
clone.push(l);
}
}
}
}
return clone;
}, []);
this.setState({ filteredContacts: filteredArray, search: search });
}
};
Any help would be much appreciated. Thanks.
When you use:
let clone = [];
at the top of the reduce() callback, you throw away the accumulator — the array that keeps getting passed in the loop which is being passed as f in your code. You should use the same reduce accumulator each time and push into it. At the end you'll have an array of all the values:
let arr = [{"Id": "8868dfdd-9b4e-4bad-a4ce-ecae6a3cc828","Name": "Company 1","Locations": [{"Id": "bd017b9c-b62e-43aa-9f00-c164a855eed1","Name": "Location 1","Departments": [{"Id": "c9e4afe3-bbdb-474f-9062-2935025bfa2e","Name": "Department 1","Employees": [{"Id": "92c3a085-5712-422d-8b0f-922b57889c4f","Name": "Employee 1","Title": "FrontEnd Engineer","Location": "New York","Photo": ""}]}]}]}]
let emp = arr.reduce((f, obj) => {
obj.Locations.forEach(location =>
location.Departments.forEach(department =>
f.push(...department.Employees.filter(emp => emp.Name == "Employee 1"))
)
)
return f
}, []) // <-- this array will get passed to every loop as `f`
console.log(emp)
EDIT based on comment
If you want to persevere the structure you can filter the arrays based on the length of the filtered array below them. Here's an example with some extra data see the filtering work, The first one is completely filtered the third has two employees with the same name. Basically it will preserve any item the has location that has a department that has a matching employee:
let arr = [
{"Id": "someother","Name": "Company 2","Locations": [{"Id": "loc2Id","Name": "Location 2","Departments": [{"Id": "d2","Name": "Department 2","Employees": [{"Id": "emp","Name": "Employee 2","Title": "FrontEnd Engineer","Location": "New York","Photo": ""}]}]}]},
{"Id": "8868dfdd-9b4e-4bad-a4ce-ecae6a3cc828","Name": "Company 1","Locations": [{"Id": "bd017b9c-b62e-43aa-9f00-c164a855eed1","Name": "Location 1","Departments": [{"Id": "c9e4afe3-bbdb-474f-9062-2935025bfa2e","Name": "Department 1","Employees": [{"Id": "92c3a085-5712-422d-8b0f-922b57889c4f","Name": "Employee 1","Title": "FrontEnd Engineer","Location": "New York","Photo": ""}]}]}]},
{"Id": "someother","Name": "Company 2","Locations": [{"Id": "loc2Id","Name": "Location 2","Departments": [{"Id": "d2","Name": "Department 2","Employees": [{"Id": "emp","Name": "Employee 1","Title": "FrontEnd Engineer","Location": "New York","Photo": ""}, {"Id": "emp","Name": "Employee 1","Title": "FrontEnd Engineer 2","Location": "New York","Photo": ""}]}]}]},
]
let f = []
let emp = arr.filter(arr =>
arr.Locations.filter(location =>
location.Departments.filter(department => {
let emp = department.Employees.filter(emp => emp.Name == "Employee 1")
return emp.length ? emp: false
}
).length
).length
) // <-- this array will get passed to every loop as `f`
console.log(emp)
Here is another short version using map:
var rx=new RegExp(search,'i'),emp=[];
obj.map(c=>
c.Locations.map(l=>
l.Departments.map(d=>
d.Employees.map(e=>
{if(e.Name.match(rx)) emp.push(e)}
))));
search contains the case-insensitive search pattern. The result is emp, an array of employee objects.
As mentioned above, map is not really necessary and could be replaced by forEach, but in my opinion it is easier to write and does not not really cause significantly more overhead.
Edit, this time using reduce():
It’s Christmas and with too much time on my hands I’ve been playing around further. The following solution will filter out the sought employees without showing their non-matching colleagues and leaving the original array intact:
const rd=(prop,fun)=>
(a,el)=>{
var arr=el[prop].reduce(fun,[]);
if(arr.length){
var r=Object.assign({},el);
// alternatively: use spread operator
// var r={...el};
r[prop]=arr;a.push(r);}
return a;}
var rx=new RegExp('employee 1','i');
var f=ma.reduce(
rd('Locations',
rd('Departments',
rd('Employees',(a,e)=>{
if(e.Name.match(rx))
a.push(e);
return a;}
,[]),[]),[]),[]);
f will contain an array containing only those locations, departments and employees where the employees will match the regular expression rx.
rd() is a generator function returning the actual filter functions that are being used at three different reduce-levels.
The static Object.assign() function is an easy way of generating a shallow object copy (similar to the slice() method for arrays).
Related
What is the best algorithm or way in comparing or cheking two different array of objects and return data if there is a match based on my problem below. I have an array of object which is on the picture below which is jobs which contains array of skills. And I have an array of skills which contains skills and i wanted to check if any of the skills from array of skills if it exist on jobs skills . And if any of the skills from array of skills exist in jobs then return jobs only which matched skills. I want that only jobs with matched skills from array of skills will be return. It will check if any item from array of skills exist in jobs and if even one or more exist then it will return jobs with match. Thank you.,
jobs (array of objects that contains array of skills)
enter image description here
skills (array of skills)
enter image description here
code in getting the job response
data.jobs_data(me, params, token).then(function (response) {
me.record.is_searching = false
if (response.status == "200") {
me.records.jobs = response.data.data
console.log("Jobs:", me.records.jobs)
var count = response.data.count
count = count + 1
me.setKeyWords(me.filters)
Pagination.init(me, count)
$timeout(function () {
me.record.is_searching = false
}, 400)
}
})
jobs skills
me.load_job_details = function (detail) {
console.log("Skills :" , me.record.job_detail.skills)
}
Try this :
var skillObj = [
"Python",
"Photoshop",
"Java Dev",
"System Administrator",
"Software Engineer"
];
var jobObj = [{
"address": "address1",
"company": "abc",
"skills": ['Software Engineer', 'Human Resource']
},{
"address": "address2",
"company": "xyz",
"skills": ['Python', 'Photoshop']
},{
"address": "address3",
"company": "alpha",
"skills": ['Data Analyst', 'BA']
}];
var res = jobObj.filter(obj => {
return skillObj.some(r=> obj.skills.indexOf(r) >= 0);
});
console.log(res);
I have an array of objects, where inside each object I have another array. I need to access the object inside that array. How do I do that?
As an example, here is my function where I log into the console each one of those arrays. And I want to console log each description instead.
const var = data.filter((u) => {
console.log(u.array)
})
And here is the JSON data
[
{
"agreed": true,
"email": "test#test.com"
"array": [
{
"name": "Alex",
"city": "Pedro",
"state": "WA",
"description": "Alex was very patient. He is one of the good guys!"
}
]
}
]
Here is the code snippet. data contains the original array then u contains each object of outer array. Then u.array.map traverses each individual array and i.description contains each sub-array's description.
data.map((u) => {
u.array.map((i) => {
console.log(i.description);
}
})
if you know the exact index, you can do this.
const var = data.filter((u) => {
console.log(u.array[0].description)
})
if you dont know the exact index, or if you wanna do this for each item in the array you can do this.
const var = data.filter((u) => {
u.array.forEach(item => {
console.log(item.description)
})
})
Well,
if this would be the structure of your Javascript Object
var data =
[
{
"agreed": true,
"email": "test#test.com"
"array": [
{
"name": "Alex",
"city": "Pedro",
"state": "WA",
"description": "Alex was very patient. He is one of the good guys!"
}
]
}
]
Then You can access the array by,
data[0].array[0].name;
And you can console.log description like this, if you are using jquery
$.each(data[0].array, function(i,v){
console.log(v.description);
})
You index into arrays with array[someIndex] starting with 0 for the first item.
So you can:
let arr = [{
"agreed": true,
"email": "test#test.com",
"array": [{
"name": "Alex",
"city": "Pedro",
"state": "WA",
"description": "Alex was very patient. He is one of the good guys!"
}]
}]
// get the first whole object
console.log(arr[0])
// get the arra property of the first object
console.log(arr[0].array)
// get the first object of that array
console.log(arr[0].array[0])
// get a property on that object
console.log(arr[0].array[0].name)
If you need to dig into an array and access of manipulate values, you can use tools like forEach, reduce(), etc. to loop over them:
let arr = [{"agreed": true,"email": "test#test.com","array": [{"name": "Alex","city": "Pedro","state": "WA","description": "Alex was very patient. He is one of the good guys!"},{"name": "Mark","city": "Anchorage","state": "AK","description": "Mark is also patient. He is one of the good guys!"}]},{"agreed": true,"email": "test#test.com","array": [{"name": "Steve","city": "New York","state": "NY","description": "Steve was very patient. He is one of the good guys!"},{"name": "Nancy","city": "Anchorage","state": "AK","description": "Nancy is also patient. She is awesome!"}]}]
// log each name
arr.forEach(obj => {
obj.array.forEach(item => {
console.log(item.name)
})
})
// build a new list of just the cities
let cities = arr.reduce((arr, obj) => {
obj.array.forEach(item => {
arr.push(item.city)
})
return arr
}, [])
console.log(cities)
You can save all descriptions to an array or just display it, like this
let descriptions = [];
data.map(item => item.array)
.forEach(array => {
array.forEach(obj => {
console.log(obj.description);
descriptions.push(obj.description);
});
});
console.log(descriptions);
I am trying to extract "animal" and "fish" hashtags from the JSON object below. I know how to extract the first instance named "animal", but I have no idea how to extract both instances. I was thinking to use a loop, but unsure where to start with it. Please advise.
data = '{"hashtags":[{"text":"animal","indices":[5110,1521]},
{"text":"Fish","indices":[122,142]}],"symbols":[],"user_mentions":
[{"screen_name":"test241","name":"Test
Dude","id":4999095,"id_str":"489996095","indices":[30,1111]},
{"screen_name":"test","name":"test","id":11999991,
"id_str":"1999990", "indices":[11,11]}],"urls":[]}';
function showHashtag(data){
i = 0;
obj = JSON.parse(data);
console.log(obj.hashtags[i].text);
}
showHashtag(data);
Use Array.prototype.filter():
let data = '{"hashtags":[{"text":"animal","indices":[5110,1521]},{"text":"Fish","indices":[122,142]}],"symbols":[],"user_mentions":[{"screen_name":"test241","name":"Test Dude","id":4999095,"id_str":"489996095","indices":[30,1111]}, {"screen_name":"test","name":"test","id":11999991, "id_str":"1999990", "indices":[11,11]}],"urls":[]}';
function showHashtag(data){
return JSON.parse(data).hashtags.filter(e => /animal|fish/i.test(e.text))
}
console.log(showHashtag(data));
To make the function reusable, in case you want to find other "hashtags", you could pass an array like so:
function showHashtag(data, tags){
let r = new RegExp(tags.join("|"), "i");
return JSON.parse(data).hashtags.filter(e => r.test(e.text))
}
console.log(showHashtag(data, ['animal', 'fish']));
To get only the text property, just chain map()
console.log(showHashtag(data, ['animal', 'fish']).map(e => e.text));
or in the function
return JSON.parse(data).hashtags
.filter(e => /animal|fish/i.test(e.text))
.map(e => e.text);
EDIT:
I don't really get why you would filter by animal and fish if all you want is an array with ['animal', 'fish']. To only get the objects that have a text property, again, use filter, but like this
let data = '{"hashtags":[{"text":"animal","indices":[5110,1521]},{"text":"Fish","indices":[122,142]}],"symbols":[],"user_mentions":[{"screen_name":"test241","name":"Test Dude","id":4999095,"id_str":"489996095","indices":[30,1111]}, {"screen_name":"test","name":"test","id":11999991, "id_str":"1999990", "indices":[11,11]}],"urls":[]}';
function showHashtag(data){
return JSON.parse(data).hashtags
.filter(e => e.text)
.map(e => e.text);
}
console.log(showHashtag(data));
For me, Lodash can be of great use here, which have different functions in terms of collections. For your case i'd use _.find function to help check the array and get any of the tags with the creteria passed in as second argument like so:
.find(collection, [predicate=.identity], [fromIndex=0])
source npm package
Iterates over elements of collection, returning the first element
predicate returns truthy for. The predicate is invoked with three
arguments: (value, index|key, collection).
with your case this should work
var data = '{ "hashtags": [ { "text": "animal", "indices": [ 5110, 1521 ] }, { "text": "Fish", "indices": [ 122, 142 ] } ], "symbols": [], "user_mentions": [ { "screen_name": "test241", "name": "Test \n Dude", "id": 4999095, "id_str": "489996095", "indices": [ 30, 1111 ] }, { "screen_name": "test", "name": "test", "id": 11999991, "id_str": "1999990", "indices": [ 11, 11 ] } ], "urls": [] }';
var obj = JSON.parse(data);
_.find(obj.hashtags, { 'text': 'animal' });
// => { "text": "animal", "indices": [ 5110, 1521 ] }
For simple parsing like this one, I would use the plain old obj.forEach() method, it is more readable and easy to understand, especially for javascript beginner.
obj = JSON.parse(data).hashtags;
obj.forEach(function(element) {
console.log(element['text']);
});
I'm working with json object literals in javascript. Starting from my original json
[{"group":"sample 1","name":"FC_TOT","value":6},
{"group":"sample 1","name":"PPC","value":88},
{"group":"sample 1","name":"PRO_OX","value":6},
{"group":"sample 1","name":"POLY_TOT","value":6}],
[{"group":"sample 2","name":"FC_TOT","value":9},
{"group":"sample 2","name":"PPC","value":8},
{"group":"sample 2","name":"PRO_OX","value":7},
{"group":"sample 2","name":"POLY_TOT","value":7}]]
I need to re-sort this based on the name and assign value to each group. The new json should look like
[
{
"group": "FC_TOT",
"sample 1": 6,
"sample 2": 9
},
{
"group": "PPC",
"sample 1": 88,
"sample 2":8
},
{
"group": "PRO_OX",
"sample 1": 6,
"sample 2": 7
},
{
"group": "POLY_TOT",
"sample 1": 6,
"sample 2": 7
}
]
where the new group is the name from the original json. Any ideas how to achieve this?
You can use reduce() to build array and inside one forEach() loop to loop objects.
var data = [[{"group":"sample 1","name":"FC_TOT","value":6},{"group":"sample 1","name":"PPC","value":88},{"group":"sample 1","name":"PRO_OX","value":6},{"group":"sample 1","name":"POLY_TOT","value":6}],[{"group":"sample 2","name":"FC_TOT","value":9},{"group":"sample 2","name":"PPC","value":8},{"group":"sample 2","name":"PRO_OX","value":7},{"group":"sample 2","name":"POLY_TOT","value":7}]]
var obj = {}
//Loop main array
var result = data.reduce(function(r, e) {
//Loop each group array to get objects
e.forEach(function(a) {
//Check if current object name exists as key in obj
if (!obj[a.name]) {
//If it doesn't create new object as its value and store it in obj
obj[a.name] = {group: a.name,[a.group]: a.value}
//push value of that object in result array of reduce or r
r.push(obj[a.name])
} else {
//If it already exists in obj then assign new property to it
Object.assign(obj[a.name], {[a.group]: a.value})
}
})
// Return accumulator or result array
return r;
}, [])
console.log(result)
I have an object array that I want to filter the data by matching it to a predicate. The complexity lies in construction of the predicate, which in this case would resemble that of a query built using a Map datastructure.
Here is what my array of objects looks like:
var data = [
{"id" : 1, "name": "bob", "address": "123 abc", "status": "active"},
{"id" : 2, "name": "henry", "address": "123 def", "status": "inactive"},
{"id" : 3, "name": "henry", "address": "123 hij", "status": "active"}
];
Here is the Map structure, from what the `predicate should be constructed:
var map = new Map<string, any>();
map.set("name", ["henry", "bob"]);
map.set("status", "active");
The correct output in this case should be:
[{"id" : 3, "name": "henry", "address": "123 hij", "status": "active"}]
Now, how should I write my data.filter((item) => {...}) routine, so that it dynamically constructs the predicate and filters the results. I do not want to hardcode the property names, as they could be any one of the properties to filter on.
Secondly, the value is of type any, which means it could a string, array or a number. The predicate is evaluating to something similar to this:
data.filter((item) =>
{ item["name"] IS IN map.get("name").values[]
&& item["status"] IS EQUAL TO map.get("status").value)});
I cannot figure out how to flatten out the Map object into a predicate and then filter the necessary. Any cues or help would be really helpful..
Pretty rough and ready, but something like the below might get you on the right track (works for your simple dataset).
const predicate = item => {
for (let [key, value] of map) {
if (!item.hasOwnProperty(key)) return false;
if (typeof value === 'number') {
if (item[key] !== value) return false;
}
if (!value.includes(item[key])) return false;
}
return true;
};