Matching two Observable Data Objects with combineLatest - javascript

I have two observable arrays: First array object observable
array1= Observable.of({
data: [
{
name: 'test',
lastname: 'last'
},
{
name: 'test1',
lastname: 'last1'
}
]
}).map(res => {
return res.data;
});
// Second Observable:
array2 = Observable.of('test1');
Expected Result from the above two array is that wherever firstname is matching with array2 value, get the lastname from that object.
//Expected Result:
object3=Observable.of('last')

It looks like you're using RxJS 5.4 so you could do it like this for example:
Observable.combineLatest(array1, array2)
.map(([users, name]) => {
const user = users.find(u => u.name === name);
return user ? user.lastname : null;
})
.subscribe(console.log);
See live demo: https://stackblitz.com/edit/rxjs5-4nyq3s?file=index.ts

Related

Merging values from an array of strings into a nested object in javascript

I want to merge values from an array into a static nested object. The array containing the values is something like this,
['name=ABC XYZ', 'hobbies=[M,N,O,P]', 'profession=S', 'age=27']
and the object in which the values has to be merged is,
const person = {
details_1: {
name: null,
hobbies: null,
profession: null
},
details_2: {
age: null
}
};
I want my output object to look like below,
const updated_person = {
details_1: {
name: 'ABC XYZ',
hobbies: [M,N,O,P],
profession: 'S'
},
details_2: {
age: 27
}
};
Thanks a lot for your help!
I made another solution with a different approach.
Here I used an interface weher I described the desired data structure.
In the second part the string array is tranformed into key and value pairs. Thereform are filtered the keys of interface and added into an empty object literal.
const data = ["name=ABC XYZ", "hobbies=[M,N,O,P]", "profession=S", "age=27"];
const dataInterface = {
details_1: { name: null, hobbies: null, profession: null },
details_2: { age: null },
};
function orederData(arr) {
const record = arr.map((item) => {
let [key, value] = item.split("=");
if (value[0] === "[" && value[value.length - 1] === "]") {
value = value.slice(1, value.length - 1).split(",");
}
return { key, value };
});
const dataBlock = {};
Object.keys(dataInterface).map((detail) => {
dataBlock[detail] = {};
Object.keys(dataInterface[detail]).forEach((dataKey) => {
dataBlock[detail][dataKey] = record.filter((record) => {
return record.key === dataKey;
})[0].value;
});
});
return dataBlock;
}
const orderedData = orederData(data);
console.log(orderedData);
You can simply achieve this by iterating the input array.
const arr = ['name=ABC XYZ', 'hobbies=[M,N,O,P]', 'profession=S', 'age=27'];
const person = {
details_1: {},
details_2: {}
};
arr.forEach(item => {
(item.split('=')[0] !== 'age') ? person.details_1[item.split('=')[0]] = item.split('=')[1] : person.details_2[item.split('=')[0]] = item.split('=')[1]
});
console.log(person);
There is no way to cleanly merge an unstructured array into a structured object such that the array values end up in the appropriately keyed person properties.
javascript does provide the assign() function that merges objects but for YOUR requirements your source data needs to be an object similarly structured and not an array.
so this:
['name=ABC XYZ', 'hobbies=[M,N,O,P]', 'profession=S', 'age=27']
would need to become this:
const source= [{details_1: {"name":"ABC XYZ", "hobbies":"[M,N,O,P]", "profession":"S"}, details_2: {"age":"27"}}]
such that a call to Object.assign():
const new_person = Object.assign(person, source[0]);
fills this
const person = {
details_1: {
name: null,
hobbies: null,
profession: null
},
details_2: {
age: null
}
};
properly, though you may need to clone or instantiate and empty person first.
or, if person is an Object you could have a fill() method that knows what to do with the array data.

Rxjs filter observable with array of filters

I'm here because i have a problem for filtering with Rxjs.
I'm trying to filter an observable of products with an array of filters...
Let me explain, I would like to set the result of my filtering to filteredProducts.
For filtering i have to check, for each filter, if the product's filter array contains the name of the filter and if the products values array's contains filter id.
For the moment, the filter works but only with the last selected filter and i'd like to filter products list with all filters in my selectedFilters array
export class ProductsFilterComponent extends BaseComponent implements OnInit {
#Select(FiltersState.getAllFilters) filters$: Observable<any>;
#Input() products$: Observable<Product[]>;
filteredProducts$: Observable<Product[]>;
public selectedFilters = [];
constructor(
private store: Store) { super(); }
ngOnInit() {
this.store.dispatch(new GetAllFilters());
}
private filterProducts() {
this.filteredProducts$ = this.products$.pipe(
map(
productsArr => productsArr.filter(
p =>
p.filters.some(f => this.selectedFilters.some(([selectedF]) => selectedF === f.name.toLowerCase()) // Filter name
&& f.values.some(value => this.selectedFilters.some(([, filterId]) => filterId === value)) // Filter id
)
)
)
);
this.filteredProducts$.subscribe(res => console.log('filtered:', res));
}
}
Here's the structure of a product object
Here's the structure of selectedFilters
A big thank you in advance :-).
I think you have to change selectedFilters to BehaviorSubject and use that with products$ observable. There is combineLatest function which listens for latest values of multiple observables and returns an array
Example
window.products$ = rxjs.of([
{
id: 1,
name: "product 1",
category: {
id: 1,
name: 'test'
},
filters: [
{
name: "test1",
values: [1]
}
],
url: '/assets/test1.png'
},
{
id: 2,
name: "product 2",
category: {
id: 1,
name: 'test'
},
filters: [
{
name: "test2",
values: [2]
}
],
url: '/assets/test2.png'
}
])
window.filteredProducts$ = null;
window.selectedFilters = new rxjs.BehaviorSubject([])
function filterProducts() {
filteredProducts$ = rxjs.combineLatest(products$, selectedFilters)
.pipe(
rxjs.operators.map(
([products, filters]) => products.filter(
product =>
product.filters.some(filter => filters.some(([filterName]) => filterName === filter.name.toLowerCase())
&& filter.values.some(value => filters.some(([, filterId]) => filterId === value))
)
)
)
);
filteredProducts$.subscribe(res => console.log('filtered:', res));
}
filterProducts()
window.selectedFilters.next([...window.selectedFilters.value, ['test1', 1]]);
window.selectedFilters.next([...window.selectedFilters.value, ['test2', 2]]);
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.5/rxjs.umd.js"></script>
Your code seems fine, but can be modified a bit.
For start, I would clean the predicate, you can use lodash intersectionWith function to intersect two arrays with different values.
Also, you can use mergeAll operator to iterate filter by filter, which makes this look less nested.
Overall it should look something like this:
products$
.pipe(
mergeAll(),
filter(
product =>
_.intersectionWith(
product.filters,
selectedFilters,
(filter, [name, id]) =>
filter.name.toLowerCase() === name && filter.values.includes(id)
).length > 0
)
)
.subscribe(console.log);
You can run the full example code here
There's is the error i get. And the lines where there's the error.

Merged array search functionality is not working

I'm having two data arrays which are coming from API and sample arrays would be like this
Array 1
[
{userId: 1
description: "Student"
imagePath: "test.png"
status: 1
}]
Array 2
[
{id: 85
accountName: "Rahul"
accountNumber: "11145678"
}
]
In my reactnative app view there's search bar and user should be able to search from these two arrays. So I merged these two arrays into one using
this.searchArray =this.filterArray[0].concat(this.filterArray[1])
So, my searchArray is a single array with Array1 and Array2 data. sample below
[
{userId: 1
description: "Student"
imagePath: "test.png"
status: 1
},
{id: 85
accountName: "Rahul"
accountNumber: "11145678"
}]
My search function is below (I need to search from account number or description)
//Search Filter
searchFilter =searchText=>{
const searchTextData = searchText.toUpperCase();
const userSearch = this.searchArray.filter(item => {
const itemData = `${item.description && item.description.toUpperCase()} ${item. accountName && item.accountName.toUpperCase()}`;
return itemData.indexOf(searchTextData) > -1;
});
}
The search functionality is not working with accountName. It's not getting any results. But if I remove ${item. accountName && item.accountName.toUpperCase()} , then it's working showing data with description. But I need to filter from both
In your array one object can have description or accountNumber so do a check if that exists include it in the itemData variable.
Try doing this
searchFilter =searchText=>{
const searchTextData = searchText.toUpperCase();
const userSearch = this.searchArray.filter(item => {
const itemData = `${item.hasOwnProperty('description'))?item.description.toUpperCase():''} ${item.hasOwnProperty('accountNumber')?item.accountNumber:''}`;
return itemData.indexOf(searchTextData) > -1;
});
}
First merge the two objects into one:
Object.keys(arr2[0]).forEach(key => {
arr1[0][key] = arr2[0][key]
})
Then create the search function:
function searchObject(obj, value){
return Object.keys(obj).some(key => {
return obj[key] === value
})
}
let arr1=[{userId:1,description:"Student",imagePath:"test.png",status:1}],arr2=[{id:85,accountName:"Rahul",accountNumber:"11145678"}];
Object.keys(arr2[0]).forEach(key => {
arr1[0][key] = arr2[0][key]
})
function searchObject(obj, prop, value){
return obj[prop] === value
}
console.log(searchObject(arr1[0], "accountName", "asdf"))
console.log(searchObject(arr1[0], "accountName", "Rahul"))

What is the most efficient functional way for refactoring forEach loop?

I have a method which accepts items and available items from props. While iteration over an array I should filter items by two conditions inside the forEach loop. The first condition is passed item if item id doesn't exist or item id equal 1. The second one should return item and filter 'description' field, otherwise, we push our items into 'nextAvailableItems' array. What is the most efficient functional way for replacing the forEach loop in this situation?
Items structure:
[{
id: 23740416,
display_name: "test",
date_from: "1970-12-31"
}]
Available items structure:
[{
id: 23740416,
display_name: "test",
description: "Text"
}]
Expected output:
[{
id: 23740416,
display_name: "test"
}]
Current code:
buildAvailableItems() {
const { items, availableItems } = this.props
const nextAvailableItems = [...availableItems]
items.forEach(item => {
if (!item.id || item.id === -1) {
return
}
const availableItem = availableItems.find(availableItem => availableItem.id === item.id)
if (availableItem) {
const { id, display_name } = availableItem
return { id, display_name }
}
const { id, display_name } = item
nextAvailableItems.push({ id, display_name })
})
return nextAvailableItems
}
buildAvailableItems() {
const { items, availableItems } = this.props
const itemIds = items.reduce((c, i) => (c[i.id] = true, c), {})
const nextAvailableItems = availableItems
.filter(a => itemIds[a.id])
.map(a => ({ id: a.id, display_name: a.display_name }))
return [...availableItems, ...nextAvailableItems]
}
First, I would turn items into an ID lookup. Then filter your availableItems using that lookup. Then do the array merge last.

Reordering array objects via javascript

I have been doing some sorting and still dont get it why the result is []. could you guys help me out what im missing?
I have my raw array object:
var data = [
{message:'hello', username:'user1'},
{message:'data', username:'user1'},
{message:'sample', username:'user2'},
{message:'here', username:'user2'},
];
my function is:
var chat = [];
function reorder(obj) {
obj.forEach( function(val, i) {
if(typeof chat[val.username] === 'undefined') {
chat[val.username] = [];
chat[val.username].push(val);
}
else
chat[val.username].push(val);
});
return chat;
}
and on my console:
reorder(data);
I am expecting to have:
var data2 = [
'user1': [{message:'hello', username:'user1'}, {message:'data', username:'user1'} ],
'user2': [{message:'sample', username:'user2'}, {message:'here', username:'user2'} ],
];
You can do this easily with reduce:
var data2 = data.reduce(function(acc, x) {
acc[x.username] = (acc[x.username] || []).concat(x)
return acc
},{})
/*^
{ user1:
[ { message: 'hello', username: 'user1' },
{ message: 'data', username: 'user1' } ],
user2:
[ { message: 'sample', username: 'user2' },
{ message: 'here', username: 'user2' } ] }
*/
The problem is that you made chat an array. When you look at an array in the console, it only displays the numeric indexes, not the named properties; you have to use console.log(chat) to see the named properties.
Since you want this to be an object with named properties, you should declare it as an object, not an array:
var chat = {};
Use Underscore's _.groupBy:
_.groupBy(data, 'username')

Categories

Resources