Angular filter array inside array - arrays

I'm working in an Angular 9 app and I need to filter an array based on another array inside each object. To understand me here is an example of the array
const products = [
{
name: 'Product 1',
origins: [
{ quantity: 1, name: 'Pack 1' },
{ quantity: 1, name: 'Pack 2' },
]
},
{
name: 'Product 2',
origins: [
{ quantity: 1, name: 'Pack 1' },
{ quantity: 1, name: 'Pack 2' },
]
},
{
name: 'Product 3',
origins: [
{ quantity: 1, name: 'Inventory' },
{ quantity: 1, name: 'Pack 5' },
]
}
]
So I got a filter input which has to filter the products by a coincidence on the name of the product or one or more origin's name.
For example, if I type "2" the result array must be:
products = [
{
name: 'Product 1',
origins: [
{ quantity: 1, name: 'Pack 2' },
]
},
{
name: 'Product 2',
origins: [
{ quantity: 1, name: 'Pack 2' },
]
}
]
Because the character "2" is in the name of origin of Product 1 and Product 2, also is present in the name "Product 2"
I tried many things to do this but the result array always modifies my original array when I put this in a pipe
<input type="text" [(ngModel)]="searchtext">
<div *ngFor="let p of (saleProducts | filter : searchtext); let i = index">
{{ p.name }}
<div *ngIf="p.origins.length > 0">
<div *ngFor="let o of p.origins">
{{ o.name }}
</div>
</div>
</div>
What is the best (simple and optimized) way to filter this using a pipe without modifying the original array?

below a pipe implementation to with a default key origins:
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'filter'
})
export class FilterPipe implements PipeTransform {
transform(value: any[], searchName: string, key = 'origins'): any[] {
const products = []
value.forEach(product => {
const matches = product[key].filter(({ name }) => name === searchName)
if (matches.length) {
products.push({ ...product, [key]: matches })
}
})
return products
}
}

Related

Grouping Data in TypeScript Array

I have JSON data that looks like this:
[
{
"id": 1,
"tags": [
"Test 1",
"Test 2",
"Test 3"
]
},
{
"id": 2,
"tags": [
"Test 2",
"Test 3",
"Test 4"
]
},
{
"id": 3,
"tags": [
"Test 3",
"Test 4"
]
}
]
I would like to transform this into data that looks like this:
[
{
"name": "Test 1",
"count": 1
},
{
"name": "Test 2",
"count": 2
},
{
"name": "Test 3",
"count": 3
},
{
"name": "Test 4",
"count": 1
}
]
I can think of some brute ways to do this, but I'm hoping there is something more performant and a little sexier? Possibly using .groupBy() or .reduce()?
Thanks for taking the time to check out my question.
I would:
parse the json
gather all tags in an array
count occurences using one of the approaches in Counting the occurrences / frequency of array elements
interface Item {
id: number,
tags: string[]
}
function countOccurences(a: string[]) {
return a.reduce(function (acc: {[key: string]: number}, curr: string) {
acc[curr] ??= 0;
acc[curr]++;
return acc;
}, {});
}
const data: Item[] = JSON.parse(json);
const tagOccurences = countOccurences(data.flatMap(o => o.tags))
Playground link
You can use reduce inside reduce to group the tags.
const array = [{
id: 1,
tags: ['Test 1', 'Test 2', 'Test 3'],
},
{
id: 2,
tags: ['Test 2', 'Test 3', 'Test 4'],
},
{
id: 3,
tags: ['Test 3', 'Test 4'],
},
];
const frequencies = Object.values(array.reduce((acc, curr) =>
curr.tags.reduce(
(nAcc, tag) => ((nAcc[tag] ??= {name: tag,count: 0}),nAcc[tag].count++,nAcc),
acc
), {}
));
console.log(frequencies);
In TypeScript:
const array = [{
id: 1,
tags: ['Test 1', 'Test 2', 'Test 3'],
},
{
id: 2,
tags: ['Test 2', 'Test 3', 'Test 4'],
},
{
id: 3,
tags: ['Test 3', 'Test 4'],
},
];
type Frequency = {
name: string,
count: number
}
const frequencies = Object.values(array.reduce((acc, curr) =>
curr.tags.reduce(
(nAcc, tag) => ((nAcc[tag] ??= {name: tag,count: 0}),nAcc[tag].count++,nAcc),
acc
), {} as Record<string, Frequency>
));
console.log(frequencies);
Playground
Using for...of iteration and a Map as a cache is a very straightforward approach... and sexy.
TS Playground
type TagsWithId = {
id: number;
tags: string[];
};
type TagCount = {
count: number;
name: string;
};
function verySexyTagCounter (input: TagsWithId[]): TagCount[] {
const map = new Map<string, number>();
for (const {tags} of input) {
for (const name of tags) {
map.set(name, (map.get(name) ?? 0) + 1);
}
}
return [...map.entries()].map(([name, count]) => ({name, count}));
}
const json = `[{"id":1,"tags":["Test 1","Test 2","Test 3"]},{"id":2,"tags":["Test 2","Test 3","Test 4"]},{"id":3,"tags":["Test 3","Test 4"]}]`;
const input: TagsWithId[] = JSON.parse(json);
const result = verySexyTagCounter(input);
console.log(result);

how to appened objects and array of objects into single array if id matches react hooks or react or react-native

if group id of API matches heading id, take Group Name from heading and menus from API and make array exactly like SampleArray.
const [newArray, setNewArray]=useState([])
let heading = [
{ ID: 3, Group Name: 'Medical Procedures' },
{ ID: 5, Group Name: 'Insurance' },
{ ID: 4, Group Name: 'Cash' },
{ ID: 6, Group Name: 'Pharmacy' },
{ ID: 7, Group Name: 'Inventory' },
{ ID: 8, Group Name: 'Lab' },
]
const settingArray=()=>{
// UserRoleList is the data from API
UserRoleList:[
{Dashboard:{GroupID:5, Menu: 'Insurance Invoice'}},
{Dashboard:{GroupID:5, Menu: 'Insurance Return'}},
{Dashboard:{GroupID:5, Menu: 'Insurance Order'}},
{Dashboard:{GroupID:8, Menu: 'Lab Order'}},
{Dashboard:{GroupID:8, Menu: 'Lab Return'}},
]
//if Groupid matches the id, add Array
setNewArray()
}
//below will be the final result which i want achieve
const [sampleArray, setSampleArray]=useState([
{
Group Name :"Insurance",
data:[
{Dashboard:{ Menu: 'Insurance Invoice'}},
{Dashboard:{ Menu: 'Insurance Return'}},
{Dashboard:{ Menu: 'Insurance Order'}},
]
},
{
Group Name :"Lab ",
data:[
{Dashboard:{ Menu: 'Lab Order'}},
{Dashboard:{ Menu: 'Lab Return'}},
]
},
])
Try this:
let heading = [
{ ID: 3, Name: 'Medical Procedures' },
{ ID: 5, Name: 'Insurance' },
{ ID: 4, Name: 'Cash' },
{ ID: 6, Name: 'Pharmacy' },
{ ID: 7, Name: 'Inventory' },
{ ID: 8, Name: 'Lab' },
];
let userRoleList = [
{Dashboard:{GroupID:5, Menu: 'Insurance Invoice'}},
{Dashboard:{GroupID:5, Menu: 'Insurance Return'}},
{Dashboard:{GroupID:5, Menu: 'Insurance Order'}},
{Dashboard:{GroupID:8, Menu: 'Lab Order'}},
{Dashboard:{GroupID:8, Menu: 'Lab Return'}}, ];
const settingArray=()=>{
//The array that will setState
const newSampleArray = [];
//The first loop in heading
heading.map(head=>{
//Data Array
const dataArray = [];
// second loop to fill data array
userRoleList.map(role =>{
if (head.ID == role.Dashboard.GroupID){
dataArray.push(role)
}
})
newSampleArray.push({GroupName:head.Name,data:dataArray})
})
//setState here
console.log(newSampleArray)
}
settingArray();
JS Fiddle Example

How can i check additional category exist when mapping this array in react js?

Here, SubCategoryDetail contains Additional Category but SubCategoryDetail may not have an Additional Category. I need to check where the Additional Category exists then go to the AdditionalCategoryDetail option if not exist then show SubCategoryDetail products when I'm mapping this array.
categoryDetail: [
{
id: 0,
SubCategoryName: "Sewing Section",
SubCategoryDetail: [
{
id: 0,
AdditionalCategoryName: "Additional Category Name 0",
AdditionalCategoryDetail: [
{
id: 0,
ProductName: 'Product Name 0',
ProductImg: "images/AllCategories/SubCategories/3.png",
Price: '80.00',
Brand: 'Brand'
},
{
id: 1,
ProductName: 'Product Name 1',
ProductImg: "images/AllCategories/SubCategories/4.png",
Price: '70.00',
Brand: 'Brand'
}]
}]
},
{
id: 1,
SubCategoryName: "Cutting Section",
SubCategoryDetail: [
{
id: 0,
ProductName: 'Product Name 0',
ProductImg: "images/AllCategories/SubCategories/2.png",
Price: '100.00',
Brand: 'Brand'
},
{
id: 1,
ProductName: 'Product Name 1',
ProductImg: "images/AllCategories/SubCategories/2.png",
Price: '100.00',
Brand: 'Brand'
}
]
}
]
}
]
You can do something like this.
results = categoryDetail.map(categories => {
return categories.SubCategoryDetail.map(category => {
if (category.hasOwnProperty("AdditionalCategoryDetail")) {
return category.AdditionalCategoryDetail[0]
} else {
return category[0]
}
})[0]
})

react-absolute-grid, displayObject issue

I am trying to use this component: https://github.com/jrowny/react-absolute-grid.
The documentation says I should pass a displayObject which renders items.
So I created a displayObject, like the one in the docs which has this render method:
render: function() {
// Supposing your item shape is something like {name: 'foo'}
const { item, index, itemsLength } = this.props;
return <div>Item {index} of {itemsLength}: {item.name}</div>;
}
I passed it to the component like this:
<AbsoluteGrid
items={SampleData.screens}
displayObject={<DisplayObject/>}
onMove={onMoveDebounced}
dragEnabled={true}
responsive={true}
verticalMargin={42}
itemWidth={250}
itemHeight={250}
filteredProp={'name'}
/>
Where SampleData.screens is:
module.exports = {
screens: [
{'url': 'http://invisionapp.com/subsystems/do_ui_kit/assets/img/screens/original-1x/screen-1-1-login.jpg', 'name': 'login', 'sort': 1, 'key': 1},
{'url': 'http://invisionapp.com/subsystems/do_ui_kit/assets/img/screens/original-1x/screen-1-2-sign-up.jpg', 'name': 'signup', 'sort': 2, 'key': 2},
{'url': 'http://invisionapp.com/subsystems/do_ui_kit/assets/img/screens/original-1x/screen-1-3-walkthrough.jpg', 'name': 'walkthrough', 'sort': 3, 'key': 3}
]
};
When I open the page in the browser, I don't see the text from the displayObject.
How can I use the displayObject?
DisplayObject works good when is a function that return the render html, I try creating a different React.Component for it but got some issues
const items = [
{ key: "0", sort: 0, name: 'Test 1', filtered: false },
{ key: "1", sort: 1 ,name: 'Test 2', filtered: false },
{ key: "2",sort: 2, name: 'Test 3', filtered: false},
{ key: "3", sort: 3,name: 'Test 4', filtered: false }
]
function GridItem(props) {
const { item, index, itemsLength } = props;
return <div >
<span>{item.name}</span>
</div>;
}
const AbsoluteGrid = createAbsoluteGrid(GridItem);

How to select multiple selected value from select option

My controller code looks like
$scope.items = [{
heading: 'Sports',
types: [{
name: 'Football',
}, {
name: 'Persie',
}, {
name: 'Ronaldo',
}, {
name: 'Messy',
}],
id: '1'
}, {
heading: 'Cricket',
types: [{
name: 'Tendulkar',
}, {
name: 'Lara',
}, {
name: 'Ponting',
}],
id: '2'
}];
My view contains something like this :
How can I get the selected values of options when user clicks submit button
Here is the jsfiddle
I used ng-repeat to build the select and ng-options to fill them, you then have to use the relative ng-model to get the selections.
HTML:
<div ng-app ng-controller="MyCtrl">
<select class="select fancy" ng-repeat="(i, item) in items" ng-model="searchOption[i]" ng-options="type.name for type in item.types"></select>
<button ng-click="submitIt()">Submit</button>
</div>
Javascript:
function MyCtrl($scope) {
$scope.submitIt = function () {
console.log($scope.searchOption);
};
$scope.searchOption = [];
$scope.items = [{
heading: 'Sports',
types: [{
name: 'Football',
}, {
name: 'Persie',
}, {
name: 'Ronaldo',
}, {
name: 'Messy',
}],
id: '1'
}, {
heading: 'Cricket',
types: [{
name: 'Tendulkar',
}, {
name: 'Lara',
}, {
name: 'Ponting',
}],
id: '2'
}];
}

Resources