I have an array of objects
const data = [{
Description: "confirm"
Id: "1"
Name: "confirm"
Value: "VIP:confirm"
}, {
Description: "validate"
Id: "2"
Name: "validate"
Value: "VIP:validate"
}, {
Description: "Sent"
Id: "2"
Name: "Sent"
Value: "VIP:Sent"
}]
Now, I am trying to get the description by passing the value:
const valuesObject = [
"VIP:Confirmed",
"VIP:Validated",
"VIP:Sent"
]
Now, Values data is like
const getDescription = (
value: string,
Values: Array < >
) => {
let allValues = _.find(Values, item => item.Value === value)
return resolve(allValues)
}
const resolve = (object) => {
return object?.Description ? object.Description : object?.Name ?? ''
}
Now, here I am doing ,
const status = valuesObject.map((value) => {
return getDescription(value, data)
})
return status.join('/')
I was expecting it should return me Confirmed/Validated/Sent
It returns but function gets called multiple times. can any one help me with this ?
Use _.intersectionWith() to get objects with the Value property that matches one of an array of values. Then map to get the Description or Name:
const getDescription = (arr, values) => _.map(
_.intersectionWith(arr, values, (o, v) => o.Value === v), // get all objects with matching values
({ Description, Name = '' }) => Description || Name // map to description / name / empty string
).join('/')
const data = [{"Description":"Confirmed","Id":"1","Name":"confirm","Value":"VIP:Confirmed"},{"Description":"Validated","Id":"2","Name":"validate","Value":"VIP:Validated"},{"Description":"Sent","Id":"2","Name":"Sent","Value":"VIP:Sent"}]
const valuesObject = ["VIP:Confirmed","VIP:Validated","VIP:Sent"]
const result = getDescription(data, valuesObject)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.19/lodash.js"></script>
Using lodash/fp you can generate the getDescription() function with _.flow():
const getDescription = _.flow(
_.intersectionWith((o, v) => o.Value === v), // get all objects with matching values
_.map(({ Description, Name = '' }) => Description || Name), // map to description / name / empty string
_.join('/')
)
const data = [{"Description":"Confirmed","Id":"1","Name":"confirm","Value":"VIP:Confirmed"},{"Description":"Validated","Id":"2","Name":"validate","Value":"VIP:Validated"},{"Description":"Sent","Id":"2","Name":"Sent","Value":"VIP:Sent"}]
const valuesObject = ["VIP:Confirmed","VIP:Validated","VIP:Sent"]
const result = getDescription(data, valuesObject)
console.log(result)
<script src='https://cdn.jsdelivr.net/g/lodash#4(lodash.min.js+lodash.fp.min.js)'></script>
Related
I need to sort array of objects by 2 conditions
sort by value
if names are the same show them next to each other in value order
Example example: https://codesandbox.io/s/relaxed-dhawan-sfryng?file=/src/index.js
I provide a solution for it,it's a bit complexed,you can read the comment I have added
const array = [
{
name: "John",
value: 5
},
{
name: "David",
value: 6
},
{
name: "John",
value: 2
},
{
name: "Michael",
value: 4
}
];
const customSort = (data) => {
// create a new array to make elements have same name together
let newArray = data.reduce((a,v) => {
let obj = a.find(e => e.name === v.name)
if(obj){
obj.datas.push(v)
}else{
a.push({'name':v.name,'datas':[v]})
}
return a
},[])
// sort data in the new array by value
newArray.forEach(e => {
e.datas.sort((a,b) => a.value - b.value)
})
// create a value object with boolean value to avoid add duplicate element
let values = data.reduce((a,v) => {
a[v.value] = false
return a
},{})
let keys = Object.keys(values)
let result = []
for(k of keys){
// if the value has iterated,then skip it
if(values[k]){
continue
}
// find data by value
let arr = newArray.filter(e1 => e1.datas.some(e2 => e2.value == k)).flatMap(e => e.datas)
result.push(...arr)
// mark the value already added
arr.forEach(e => {
values[e.value] = true
})
}
return result
}
console.log(customSort(array))
Hope it helps:
const arr = [
{
name: "John",
value: 5
},
{
name: "David",
value: 6
},
{
name: "John",
value: 2
},
{
name: "Michael",
value: 4
}
];
const groupByItems = [];
// Get index of the item based on name in groupByItems array
const getIndex = (name) => {
let index = -1;
groupByItems.forEach((groupByItem, _index) => {
if(groupByItem.name === name) {
index = _index;
}
});
return index;
}
// Group items by their name
const groupByName = () => {
arr.forEach((item) => {
const name = item.name;
const value = item.value;
let index = getIndex(name);
//Means that the [name] has not added before, so we should add it
if(index === -1) {
groupByItems.push({
name: name,
// Hold all values of the [name]
values: [],
// Hold minValue to sort groupByItems by that
minValue: Infinity
});
index = groupByItems.length - 1;
}
// Add current value to the list of values
groupByItems[index].values.push(value);
// Update minValue
if(groupByItems[index].minValue > value) {
groupByItems[index].minValue = value;
}
});
}
groupByName();
//Sort by minValue and then return final objects
const result = groupByItems.sort((a, b) => a.minValue - b.minValue).flatMap((item) => (
item.values.sort((firstValue, secondValue) => firstValue - secondValue).map((value) => {
return {
name: item.name,
value: value
}
})
));
console.log(result);
Use Case 1
Assuming i have 2dArray Object of
let arr = [{'getName':'Report1'},{'getName':'User'},{'getName':'report 2'},{'getName':'User'},{'getName':'User'}]
let _NotRequiredSheet = ['User','Report 254',...]
Im trying to optimise my script with functional programming which will return me an array of
['report1','report2']
The current Method im using which does not have any error is :
for(let i =0;i < arr.length;i++){
if(arr[i].getName != _NotRequiredSheet[0]){
console.log(arr[i].getName)
}
}
But this will impact if _notrequiredSheet have a big list of what is not required
I tried using this approach which is using filter but since its 2dObject Array, im unsure how should this be implemented.
What i did on my poc is
//Approach 1 : Not Working
let result = arr.filter(function (arr) {
return arr.getName != _NotRequiredSheet.values();
})
//Output should be as 1dArray['report1','report2'] , not (5) [{…}, {…}, {…}, {…}, {…}]
console.log(result)
//Approach 2 : Will output as 2D array with filtered value
// Will require to hardcord the index which is not advisable
let result = arr.filter(function (arr) {
return arr.getName != _NotRequiredSheet[0];
})
console.log(result)
i wanted to check if there is any way i could pass on using for loop with filter function. Result should return as 1D array which is
['Report1','Report2']
Use case 1 is Solved
Use Case 2 : 2D Object Array
Assuming data is declared as
let arr2 = [
{$0:{'Name':'Report1'}},
{$0:{'Name':'Report2'}},
{$0:{'Name':'User'}}
]
Result should show this on console.log (2) [{…}, {…}] , filter function will remove 'User' as its reflected in _NotRequiredSheet.
Using the syntax i wrote
let result = arr2.map(item => item.$0.Name).filter(Name => !_NotRequiredSheet.includes(Name))
This will return as a single array
You could filter your data with looking for unwanted values and map only the wanted property.
const
data = [{ getName: 'Report1' }, { getName: 'User' }, { getName: 'report 2' }, { getName: 'User' }, { getName: 'User' }],
_NotRequiredSheet = ['User', 'Report 254'],
result = data
.filter(({ getName }) => !_NotRequiredSheet.includes(getName))
.map(({ getName }) => getName);
console.log(result);
With a Set
const
data = [{ getName: 'Report1' }, { getName: 'User' }, { getName: 'report 2' }, { getName: 'User' }, { getName: 'User' }],
_NotRequiredSheet = ['User', 'Report 254'],
take = k => o => o[k],
hasNot = s => v => !s.has(v),
comp = f => g => o => f(g(o)),
result = data
.filter(
comp(hasNot(new Set(_NotRequiredSheet)))(take('getName'))
)
.map(({ getName }) => getName);
console.log(result);
I'd recommend using reduce()
so you can return something based on _NotRequiredSheet.includes(cur.getName)
let arr = [{'getName':'Report1'},{'getName':'User'},{'getName':'report 2'},{'getName':'User'},{'getName':'User'}]
let _NotRequiredSheet = ['User','Report 254' ];
let res = arr.reduce((prev, cur) => {
if (_NotRequiredSheet.includes(cur.getName)) {
return prev;
} else {
return [ ...prev, cur.getName ];
}
}, []);
console.log(res);
I am creating searchbar to filter key value data objects. I am getting filter is not a function error while doing search. Is there any other function to filter in key value pair objects ?
Data :-
{
meta_city : {
label: "City"
values: (5) ["DL", "KA", "GJ", "MH", "UP"]
},
meta_country : {
label: "Country"
values: (5) ["IN", "US", "CA"]
}
}
Handle search (filterData data is local state) :-
const handleSearchFilter = (event) => {
const searchWord = event.target.value;
const newFilter = Object.keys(filterData).map((key) => {
filterData[key].filter((value) => {
return value.includes(searchWord);
});
});
setFilterData(newFilter);
};
<div className="mp-input-field-container">
<input
className="mp-input"
placeholder="Search"
onChange={handleSearchFilter}
/>
</div>
You should use reduce like this:
const handleSearchFilter = (event) => {
const searchWord = event.target.value;
const newFilter = Object.keys(filterData).reduce((result, key) => {
if (filterData[key].values.includes(searchWord)) {
result.push(filterData[key]);
};
return result;
}, []);
setFilterData(newFilter);
};
In this example I'm returning an array result. you can return an object if you want.
Filter does not exist on a string type. When filterData[key] is called, key has a value of label. filterData["label"] returns a string "City".
try
const searchWord = (word) => Object.values(filterData).filter((data) => data.values?.includes(word));
const handleSearchFilter = (event) => {
const word = event.target.value;
const [newFilter] = searchWord(word)
setFilterData(newFilter);
}
searchWord returns if you search "DL"
[
{
label: 'City',
values: [ 'DL', 'KA', 'GJ', 'MH', 'UP' ]
}
]
Was that the result you were looking for?
Here is the code snippet to prove the solution works:
var filterData = {
meta_city : {
label: "City",
values: ["DL", "KA", "GJ", "MH", "UP"]
},
meta_country : {
label: "Country",
values: ["IN", "US", "CA"]
}
}
const searchWord = (word) => Object.values(filterData).filter((data) => data.values.includes(word));
console.log(searchWord("DL"))
I need to search data in an array according to filter so I am able to do it I am searching data according to name and id but in the backend in index 0 I am not getting and name and id only getting one number so when I search data I am getting an undefined error so what my task is I need to set filter to start searching from index 1 mean ignore index 0
coming data example from a backend
[
2,
{
search_id: "10000107",
name: "dev name",
},
{
search_id: "10000106",
name: "alberto",
},
]
function handleSearch(term) {
const dummy = props.new_alert_list.filter((item) =>
item.name.toLowerCase().includes(term)
);
const dummy1 = props.new_alert_list.filter((item) =>
item.search_id.includes(term)
);
const no = parseInt(term);
if (isNaN(no)) setItems(dummy);
else setItems(dummy1);
}
Yes you could start from element at index 1, but I think you could also use hasOwnProperty. Something like:
let data = [
2,
{
id: "10000107",
name: "dev name",
},
{
id: "10000106",
name: "alberto",
},
]
data.filter(x => {
if (x.hasOwnProperty("id") && x.hasOwnProperty("name")) {
console.log(x.id, " ", x.name); // if object in array has id and name properties then do stuff...
}
});
.filter() has a second parameter "index", which you can use.
For example:
[1,2,3].filter((element, index) => { return index != 0 });
Source: https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
You can pass another parameter index in the filter()
function handleSearch(term) {
const dummy = props.new_alert_list.filter((item, index) =>
index !== 0 ? item.name.toLowerCase().includes(term) : false
);
const dummy1 = props.new_alert_list.filter((item, index) =>
index !== 0 ? item.search_id.includes(term) : false
);
const no = parseInt(term);
if (isNaN(no)) setItems(dummy);
else setItems(dummy1);
Slice the array from index 1 to create a new array to filter from.
function handleSearch(term) {
const dummy = props.new_alert_list.slice(1).filter((item) =>
item.name.toLowerCase().includes(term)
);
const dummy1 = props.new_alert_list.slice(1).filter((item) =>
item.search_id.includes(term)
);
const no = parseInt(term);
if (isNaN(no)) setItems(dummy);
else setItems(dummy1);
}
Alternatively you could access the current index being iterated (the second parameter to filter) and add a condition for non-zero (i.e. truthy) indices.
function handleSearch(term) {
const dummy = props.new_alert_list.filter((item, index) =>
index && item.name.toLowerCase().includes(term)
);
const dummy1 = props.new_alert_list.filter((item, index) =>
index && item.search_id.includes(term)
);
const no = parseInt(term);
if (isNaN(no)) setItems(dummy);
else setItems(dummy1);
}
If the first element is irrelevant in the frontend UI then perhaps you should clear it out before sending in props, or better yet, remove it before saving it into any local component state.
You can just sniff out if the item in the iterator has the properties. If so, then filter
function handleSearch(term) {
setItems(props.new_alert_list
.filter(item =>
(typeof item === "object" &&
(item.hasOwnProperty('name') && item.name.toLowerCase().includes(term.toLowerCase()))
||
(item.hasOwnProperty('search_id') && item.search_id.includes(term))
));
}
let data = [
2, {
search_id: "10000107",
name: "dev name",
},
{
search_id: "10000106",
name: "alberto",
},
{},
{
name: "Joe"
}
]
let term = 'alberto';
let filtered = data.filter(item => (typeof item === "object" && (item.hasOwnProperty('name') && item.name.toLowerCase().includes(term.toLowerCase())) || (item.hasOwnProperty('search_id') && item.search_id.includes(term))));
console.log(filtered);
I have a source array and target array, based on the target array need to update the source array
sourceAry = [{name:'Label1', value: 'label1', children:[{name:'Ammu'},{name:'Rahual'},{name:'Anu'}]},
{name:'Label2', value: 'label2', children:[{name:'Hari'},{name:'Tom'}]},
];
targetAry = [{name:'Label1', value: 'label1', children:[{name:'Anu'}]},
{name:'Label2', value: 'label2', children:[{name:'Hari'},{name:'Tom'}]},
];
resultAry = [{name:'Label1', value: 'label1', children:[{name:'Ammu'},{name:'Rahual'}]}
]},
];
Code which I try
let resultAry = sourceAry.map((obj) => {
obj.children.map((elem) =>{
targetAry.filter(parent => parent.children.filter((el) => {
el.name !== elem.name}))
})
})
console.log(resultAry, 'NEW', list);
you could start start with some facilities to make it simpler:
const indexBy = (f, data) => data.reduce((acc, x) => Object.assign(acc, { [f(x)]: x }), {})
const remove = (keyFn, dataToRemove, from) => {
const dataToRemoveIndexed = indexBy(keyFn, dataToRemove);
return from.filter(it => !(keyFn(it) in dataToRemoveIndexed));
}
we introduce the indexBy here, to make removal O(m+n), instead of O(m^2) (if there are many items in the collection to check)
then you can use it like this:
const targetIndexed = indexBy(it => it.name, targetAry);
const result = sourceAry.map(
it => ({
...it,
children: remove(
it => it.name,
(targetIndexed[it.name] || {}).children || [],
it.children
)
})
)
so it leaves you with the following result:
[
{"name":"Label1","value":"label1","children":[{"name":"Ammu"}, {"name":"Rahual"}]},
{"name":"Label2","value":"label2","children":[]}
]
if you also want to delete the item with empty children, you can just filter it out: result.filter(it => it.children.length > 0)
Ciao, try something like this:
sourceAry = [{name:'Label1', value: 'label1', children:[{name:'Ammu'},{name:'Rahual'},{name:'Anu'}]}, {name:'Label2', value: 'label2', children:[{name:'Hari'},{name:'Tom'},{name:'Ammu'},{name:'Rahual'},{name:'Anu'}]}, {name:'Label3', value: 'label3', children:[{name:'Ammu'},{name:'Rahual'},{name:'Anu'}]} ];
targetAry = [{name:'Label1', value: 'label1', children:[{name:'Anu'}]},
{name:'Label2', value: 'label2', children:[{name:'Hari'},{name:'Tom'}]},
];
let result = [];
sourceAry.forEach(source => {
let filter = targetAry.filter(target => target.name === source.name)
if (filter.length > 0) {
let filterchildren = source.children.filter(a => !filter[0].children.map(b=>b.name).includes(a.name));
if (filterchildren.length > 0) {
let resultobj = source;
resultobj.children = filterchildren;
result.push(resultobj);
}
}
else result.push(source);
})
console.log(result)
I filter targetAry based on sourceAry name. Then subtract children with .filter(a => !filter[0].children.map(b=>b.name).includes(a.name)); and finally push element found in result array.