Search/Filter nested Array Javascript/Lodash - javascript

For my React.js project I would like to create a search-filter of a nested Array. Users will search with an input-field.
var dataExample = [
{
type: "human", details: [
{id: 1, name: "Peter", description: "friendly, black-hair"},
{id: 5, name: "Susan", description: "blond"}
]
},
{
type: "animal", details: [
{id: 2, name: "Will", description: "lazy, cute"},
{id: 3, name: "Bonny", description: "beautiful"}
]
}
];
In my search-input-field I want to look for "name" or something in "description". The data structure of the array should remain the same.
The output when I'm searching for "friendly" or "Peter" should be:
[
{
type: "human", details: [
{id: 1, name: "Peter", description: "friendly, black-hair"}
]
}
];
Now I tried something like this:
let myfilter = dataExample.filter((data) => {
data.details.filter((items) => {
return (items.type.indexOf("human") !== -1 || //input of user
items.description.indexOf("friendly"))
})
})
Unfortunately, this is not how it works. Can anybody help me? Lodash would be no problem, too. Thank you so much.

You can use array#reduce with array#filter and to check for your word you can use string#incldues.
const dataExample = [ { type: "human", details: [ {id: 1, name: "Peter", description: "friendly, black-hair"}, {id: 5, name: "Susan", description: "blond"} ] }, { type: "animal",details: [ {id: 2, name: "Will", description: "lazy, cute"}, {id: 3, name: "Bonny", description: "beautiful"} ] } ],
term = 'Peter',
result = dataExample.reduce((r, {type,details}) => {
let o = details.filter(({name,description}) => name.includes(term) || description.includes(term));
if(o && o.length)
r.push({type, details : [...o]});
return r;
},[]);
console.log(result);

Here are some examples without lodash.
var dataAll = [
{
type: "human",
details: [
{id: 1, name: "Peter", description: "friendly, black-hair"},
{id: 5, name: "Susan", description: "blond"}
]
},
{
type: "animal",
details: [
{id: 2, name: "Will", description: "lazy, cute"},
{id: 3, name: "Bonny", description: "beautiful"}
]
}
];
var entryTypeFilter = data => data.type.indexOf("hum") !== -1;
var entryDetailDescFilter = data => data.description.indexOf("friend") !== -1;
var entryDetailsMapper = data => {
return {
type: data.type,
details: data.details.filter(entryDetailDescFilter)
};
};
var entryNoDetailsFilter = data => data.details && data.details.length !== 0;
var dataFilteredByType = dataAll.filter(entryTypeFilter);
var dataFilteredByDesc = dataAll.map(entryDetailsMapper);
var dataFilteredByTypeAndDesc = dataAll.filter(entryTypeFilter).map(entryDetailsMapper);
var dataFilteredByDescTrimmingEmptyDetailEntries = dataAll.map(entryDetailsMapper).filter(entryNoDetailsFilter);
In modern javascript you might want to search on how to use the ... keyword for the mapping callback functions.

Related

Append/Add the property to array of objects if match found using javascript

I have two array of objects, in which if id and aid property values match then append the property code to arr1 and return the result
var arr1 = [
{ id: 1, name: "xxx", cn: "IN" },
{ id: 2, name: "yyy", cn: "MY" },
{ id: 3, name: "zzz", cn: "SG" },
]
var arr2 = [
{ aid: 1, code: "finance" },
{ aid: 2, code: "others" },
{ aid: 4, code: "finance" },
{ aid: 5, code: "product" },
]
Expected result:
var arr1 = [
{ id: 1, name: "xxx", cn: "IN", code: 'finance'},
{ id: 2, name: "yyy", cn: "MY", code: 'others'},
{ id: 3, name: "zzz", cn: "SG", code: ''},
]
I tried
var result = arr1.map(e=> ({
...e,
code: arr2.map(i=>i.code)
})
You can do what you require with a loop over arr1 looking for matches in arr2 using filter(). This will add the code property to the existing arr1 array without creating a new array instance.
Note that this logic assumes that there will only ever be 0 or 1 matches in arr2.
var arr1 = [
{ id: 1, name: "xxx", cn: "IN" },
{ id: 2, name: "yyy", cn: "MY" },
{ id: 3, name: "zzz", cn: "SG" },
]
var arr2 = [
{ aid: 1, code: "finance" },
{ aid: 2, code: "others" },
{ aid: 4, code: "finance" },
{ aid: 5, code: "product" },
]
arr1.forEach(o1 => o1.code = arr2.filter(o2 => o2.aid === o1.id)[0]?.code || '');
console.log(arr1);
I am not sure if this is the most optimal solution but this definitely works for your case:
let result = arr1.map((outerObj) => {
// First we try to find the index of the matching object
let index = arr2.findIndex((innerObj) => {
return outerObj.id == innerObj.aid;
});
// In case of no match, set case as "" as per requirement
if(index == -1) {
outerObj.code = "";
return outerObj;
}
// If a match is found, then combine the two objects.
let mergedObj = { ...outerObj, ...arr2[index]};
// aid is not needed in the result, Who wants aids anyway?
delete mergedObj.aid;
return mergedObj;
});
If not more optimal, then definitely there could be smaller code but I think the code should be clear and readable rather than being smaller.
First, massage the data into a format for fast manipulation; then manipulate.
The following:
Puts every item in arr2 into a map, keyed on aid
Enumerates arr1 once, looking into the map for each item to see if there is a corresponding code
Performance: O(N).
var arr1= [
{id:1, name: "xxx", cn: "IN"},
{id:2, name: "yyy", cn: "MY"},
{id:3, name: "zzz", cn: "SG"} ]
var arr2 = [
{ aid: 1, code: "finance"},
{ aid: 2, code: "others"},
{ aid: 4, code: "finance"},
{ aid: 5, code: "product"} ]
const map = arr2.reduce((p,c) => p.set(c.aid, c), new Map)
arr1.forEach((i) => i.code = map.get(i.id)?.code || '')
console.log(arr1)
You need to first find matching item in arr2 and them create the composed item.
Try like this:
var arr1 = [
{ id: 1, name: "xxx", cn: "IN" },
{ id: 2, name: "yyy", cn: "MY" },
{ id: 3, name: "zzz", cn: "SG" },
];
var arr2 = [
{ aid: 1, code: "finance" },
{ aid: 2, code: "others" },
{ aid: 4, code: "finance" },
{ aid: 5, code: "product" },
];
const result = arr1.map((item) => {
const match = arr2.find((o) => o.aid === item.id);
return { ...item, code: match ? match.code : '' };
});
console.log(result);
we can get like this too
var arr1 = [
{ id: 1, name: "xxx", cn: "IN" },
{ id: 2, name: "yyy", cn: "MY" },
{ id: 3, name: "zzz", cn: "SG" },
]
var arr2 = [
{ aid: 1, code: "finance" },
{ aid: 2, code: "others" },
{ aid: 4, code: "finance" },
{ aid: 5, code: "product" },
]
const fun = (ar, ar2)=>{
const getResult = ar.map((e)=> {
const Data ={
id : e.id,
name : e.name,
cn : e.cn,
code : ar2.find((e2)=> e2.aid===e.id)?.code || ""
}
return Data;
})
return getResult
}
console.log(fun(arr1, arr2))
we can usefind method too
var arr1 = [
{ id: 1, name: "xxx", cn: "IN" },
{ id: 2, name: "yyy", cn: "MY" },
{ id: 3, name: "zzz", cn: "SG" },
]
var arr2 = [
{ aid: 1, code: "finance" },
{ aid: 2, code: "others" },
{ aid: 4, code: "finance" },
{ aid: 5, code: "product" },
]
arr1.forEach(e => e.code = arr2.find(d => d.aid === e.id)?.code || '');
console.log(arr1);

Get specific value from array object in a single line in typescript

I have a following array
const _array = [{id: 1, name: 'Adam'}, {id:3, name: 'Crystal'}, {id:2, name: 'Bob'}, {id: 4, name: 'Daisy'}];
How to write a single line of code in typescript to get item where name equal to Crystal from the array?
You can use array find method like following:
const _array = [
{ id: 1, name: "Adam" },
{ id: 3, name: "Crystal" },
{ id: 2, name: "Bob" },
{ id: 4, name: "Daisy" },
];
const item = _array.find((item) => item.name === "Crystal");
console.log(item);
Output
{ id: 3, name: 'Crystal' }

Accessing nested array in a json

let ages = data
.filter(isDog)
.map(dogYears)
.reduce(sum);
mL/hr
i want to find the best way of accessing array elements in a javascript object.
Eg: I want to find the first faculty name & first specializations for each course.
var students =
{
deptartment:[
{
name:'Computer Science',
age:20,
Course:[
{ id: 100000
name:'Object Oriented Programming',
faculty:[
{
id:123,
name:'John',
Specialization: [
{name: 'science'},
{name: 'Physics'}
]
}
]
},
{ id: 100001
name:'C#',
faculty:[
{
id:124,
name:'Denis',
Specialization: [
{name: 'Ecnonomics'},
{name: 'Physics'}
]
}
]
}
],
}
]
};
I know i can get the faculty name and specialization by
var courses= deptartment && deptartment.Course ;
var facultyWithSpecialization= {};
if(courses){
courses.forEach(course =>{
var fname = course.faculty && course.faculty[0].name;
var s= course.faculty && course.faculty.Specialization;
facultyWithSpecialization[fname] = s && s[0].name;
})
}
use Object.assign({}, deptartment.Course) instead of department.Course
tried to use the below code but it doesn't make much difference.
var courses=Object.values(Object.assign({}, deptartment.Course));
var fname = Object.values(Object.assign({}, course.faculty[0].Specialization[0]));
Expecting
'John': 'science'
'Denis': 'Ecnonomics'
You can try this. There were many error in the object including spelling mistakes and formatting
var students = {
deptartment: [{
name: 'Computer Science',
age: 20,
Course: [{
id: 100000,
name: 'Object Oriented Programming',
faculty: [{
id: 123,
name: 'John',
Specialization: [{
name: 'science'
},
{
name: 'Physics'
}
]
},
{
id: 124,
name: 'Denis',
Specialization: [{
name: 'Ecnonomics'
},
{
name: 'Physics'
}
]
}
]
}],
}]
}
var obj = {};
students.deptartment.forEach((e) => {
e.Course.forEach((k) => {
k.faculty.forEach((l) => {
obj[l.name] = l.Specialization[0].name
})
})
})
console.log(obj)
I think you meant department instead of deptartment.
I modified a bit your JSON as it was a bit buggy:
var students = {
departments:[
{
name:'Computer Science',
age:20,
Courses:[
{ id: 100000,
name:'Object Oriented Programming',
faculty:[
{
id:123,
name:'John',
Specialization: [
{name: 'science'},
{name: 'Physics'}
]
},
{
id:124,
name:'Denis',
Specialization: [
{name: 'Ecnonomics'},
{name: 'Physics'}
]
}
]
}
],
}]
}
You can use map to achieve this nesting:
students.departments.map(
department => department.Courses.map(
course => course.faculty.map(
student => ({
name: student.name,
specialization: student.Specialization[0].name // check nulls here!
})
)
)
)

Categorize Similar items into separate objects from Array lists

I have an array of items that I get from API as a response body.
data = [{id: 1, category: "kitchen", name: "noodles"},
{id: 2, category: "general", name: "Wi-Fi"},
{id: 3, category: "sports", name: "Football"},]
I want to iterate over the arrays, and get the data like :
var categorized = {
kitchen: [{id: 1, category: "kitchen", name: "noodles"}],
general : [{id: 2, category: "general", name: "Wi-Fi"}],
sports : [{id: 3, category: "sports", name: "Football"}]
};
Is there any lodash methods, or any ES6 shortcuts for this ?
In answer to your question 'is there a lodash method?' Yes: https://lodash.com/docs/4.17.4#groupBy. For your specific example:
const categorized = _.groupBy(data, 'category');
Edit: You could roll your own groupBy type function with ES6 as in another example. But if you are using lodash anyway this is a whole lot cleaner.
I used array.reduce to get the structure
var data = [{
id: 1,
category: "kitchen",
name: "noodles"
}, {
id: 2,
category: "general",
name: "Wi-Fi"
}, {
id: 3,
category: "sports",
name: "Football"
}]
var newData = data.reduce(function(obj, v, i) {
obj[v.category] = obj[v.category] || [];
obj[v.category].push(v);
return obj;
}, {});
console.log(newData);
In ES6 you could so using:
var newData = data.reduce((obj, v, i)=> {
obj[v.category] = obj[v.category] || [];
obj[v.category].push(v);
return obj;
}, {});
console.log(newData);

Get all values in an object array with specific key (set::extract cakephp equivalent)

is there an equivalent of cakePHPs set::extract functionality (http://book.cakephp.org/2.0/en/core-utility-libraries/set.html#Set::extract)?
What i've got is something like that:
var arr = [{name: "test", children: [id: 1, title: "title1"]},
{name: "test2", children: [id: 2, title: "title2"]},
{name: "lipsum", children: [id: 1, title: "title1", id: 2, title: "title2"]},
{name: "lipsum2", children: [id: 3, title: "title3"]}]
What i want to do is:
var objs = filter({arr.children.id:1});
The result of 'objs' should be:
[{name: "test", children: [id: 1, title: "title1"]},
{name: "lipsum", children: [id: 1, title: "title1", id: 2, title: "title2"]}]
Thanks in advance.
You have an invalid object structure... that children array should actually be an object. Then you can do:
var filtered = arr.filter(function(item) {
return item.children.filter(function(child) {
return child.id === 1;
}).length
});
Correct data format:
[{
name: "test",
children: [{
id: 1,
title: "title1"
}]
}]
Demo: http://jsfiddle.net/588o5p69/
The correct structure for your array in JS would have to be this:
var arr = [{name: "test", children: [ {id: 1, title: "title1" } ]},
{name: "test2", children: [ { id: 2, title: "title2" } ]},
{name: "lipsum", children: [ {id: 1, title: "title1"}, {id: 2, title: "title2"}]},
{name: "lipsum2", children: [ {id: 3, title: "title3"} ]}];
On that structure, you can simply use underscore's _.filter:
var resArr= _.filter(arr, function(elem){ return elem.children.id === 1; });
If IE < 9 is not relevant, you can also go with ECMA Script 5's native filter function:
var resArr= arr.filter(function(elem){ return elem.children.id === 1; });

Categories