RXJS data manipulation - arrays

I am trying to find the reactive way of manipulating data in an array of objects. In the array below, I want to determine the low score in groups of four and add a low and diff property to the item.
arr = [
{ name: 'Cliff', score: 44 },
{ name: 'Bob', score: 55 },
{ name: 'Jack', score: 404 },
{ name: 'John', score: 50 },
{ name: 'Doug', score: 22 },
{ name: 'EZ', score: 550 },
{ name: 'Bill', score: 404 },
{ name: 'Dan', score: 5 },
{ name: 'Rich', score: 404 },
{ name: 'Steve', score: 565 },
];
arr$: Observable<Arr[]> = of(this.arr);
getData() {
const scores = this.arr$.pipe(
map((result) =>
result.map((data) => {
return {
x: data.score,
};
})
)
// reduce((acc, x) => (acc = acc > x.score ? acc :x.score), 0)
);
return scores;
}
}
/*
Desired result
arr = [
{ name: 'Cliff', score: 44, low: 44, diff 0 },
{ name: 'Bob', score: 55, low: 44, diff: 9 },
{ name: 'Jack', score: 404, low 44, diff: 360 },
{ name: 'John', score: 50, low: 44, diff: 6 },
{ name: 'Doug', score: 22, low 5, diff: 17 },
{ name: 'EZ', score: 550, low 5, diff: 545 },
{ name: 'Bill', score: 404, low 5, diff: 399 },
{ name: 'Dan', score: 5, low 5, diff: 0 },
{ name: 'Rich', score: 404, low: 404, diff: 0 },
{ name: 'Steve', score: 565. low 404, diff: 121 },
];
*/
I can't seem to get past the few lines of code that I've written. A StackBlitz is here.

This could be a reactive way to get what you want. The comments in the code explain the logic.
// the from rxjs function takes an array and returns an
// Observable that emits each item in the array
from(arr)
.pipe(
// bufferCount will emit an array of arrays of items,
// therefore creates the groups of four items
bufferCount(4),
// for each group we find the low and create
// the new items with the low value and the diff
map((buff) => {
const low = buff.reduce((acc, curr) => {
return acc < curr.score ? acc : curr.score;
}, Infinity);
return buff.map((v) => ({ ...v, low, diff: v.score - low }));
}),
// with mergeMap we flatten the groups of items
// and emit each item
mergeMap((buffEnriched) => buffEnriched),
// if you want to emit an array rather than each single
// object you add toArray
toArray()
)

it makes more sense to sort, then generate your data. otherwise, your output data in your question makes little sense to me.
// DATA IN
arr = [
{ name: 'Cliff', score: 44 },
{ name: 'Bob', score: 55 },
{ name: 'Jack', score: 404 },
{ name: 'John', score: 50 },
{ name: 'Doug', score: 22 },
{ name: 'EZ', score: 550 },
{ name: 'Bill', score: 404 },
{ name: 'Dan', score: 5 },
{ name: 'Rich', score: 404 },
{ name: 'Steve', score: 565 },
];
// PROCESSING and OUTPUT
let lowest = arr[0].score;
let output_arr = arr.sort((a, b)=>{
if(a.score < b.score) return -1;
if(a.score > b.score) return 1;
return 0;
}).map((a)=>{
if(a.score < lowest) lowest = a.score;
return {
name: 'Steve',
score: 565,
low: lowest,
diff: a.score - lowest
};
});
// use output array output
console.log(output_arr);
DEMO
Example
INPUT
[
{ name: 'Cliff', score: 44 },
{ name: 'Bob', score: 55 },
{ name: 'Jack', score: 404 },
{ name: 'John', score: 50 },
{ name: 'Doug', score: 22 },
{ name: 'EZ', score: 550 },
{ name: 'Bill', score: 404 },
{ name: 'Dan', score: 5 },
{ name: 'Rich', score: 404 },
{ name: 'Steve', score: 565 },
]
OUTPUT
[{
diff: 0,
low: 5,
name: "Steve",
score: 565
}, {
diff: 17,
low: 5,
name: "Steve",
score: 565
}, {
diff: 39,
low: 5,
name: "Steve",
score: 565
}, {
diff: 45,
low: 5,
name: "Steve",
score: 565
}, {
diff: 50,
low: 5,
name: "Steve",
score: 565
}, {
diff: 399,
low: 5,
name: "Steve",
score: 565
}, {
diff: 399,
low: 5,
name: "Steve",
score: 565
}, {
diff: 399,
low: 5,
name: "Steve",
score: 565
}, {
diff: 545,
low: 5,
name: "Steve",
score: 565
}, {
diff: 560,
low: 5,
name: "Steve",
score: 565
}]

Related

TypeError: Cannot destructure property 'title' of 'collections' as it is undefined

I am trying to get collections using selectors. Here is my code:
Selectors
import { createSelector } from "reselect";
export const selectShop = state => state.shop;
export const selectCollections = createSelector(
[selectShop],
shop => shop.collections
)
export const selectCollectionForPreview = createSelector(
[selectCollections],
collections => Object.keys(collections).map(key => collections[key])
)
export const selectCollectionItem = collectionUrl =>
createSelector(
[selectCollections],
collections => collections[collectionUrl]
//It seems that the selector above is returning collections as an array instead of object, so collections[collectionUrl] is returning undefined
)
Component
const CollectionPage = ({collections}) => {
// const {title, items} = Object.keys(collections)
const {title, items} = collections
console.log('collection', collections);
return(
// <></>
<div className="collection-page">
<h2 className='title'>{title}</h2>
<div className='items'>
{items.map( item =>
<CollectionItem key={item.id} item={item}/>
)}
</div>
</div>
)
}
const mapStateToProps = (state,ownProps) => ({
collections: selectCollectionItem(ownProps.match.params.collectionId)(state)
})
export default connect(mapStateToProps)(CollectionPage);
SHOP_DATA
const SHOP_DATA = {
hats:{
id: 1,
title: 'Hats',
routeName: 'hats',
items: [
{
id: 1,
name: 'Brown Brim',
imageUrl: 'https://i.ibb.co/ZYW3VTp/brown-brim.png',
price: 25
},
{
id: 2,
name: 'Blue Beanie',
imageUrl: 'https://i.ibb.co/ypkgK0X/blue-beanie.png',
price: 18
},
{
id: 3,
name: 'Brown Cowboy',
imageUrl: 'https://i.ibb.co/QdJwgmp/brown-cowboy.png',
price: 35
},
{
id: 4,
name: 'Grey Brim',
imageUrl: 'https://i.ibb.co/RjBLWxB/grey-brim.png',
price: 25
},
{
id: 5,
name: 'Green Beanie',
imageUrl: 'https://i.ibb.co/YTjW3vF/green-beanie.png',
price: 18
},
{
id: 6,
name: 'Palm Tree Cap',
imageUrl: 'https://i.ibb.co/rKBDvJX/palm-tree-cap.png',
price: 14
},
{
id: 7,
name: 'Red Beanie',
imageUrl: 'https://i.ibb.co/bLB646Z/red-beanie.png',
price: 18
},
{
id: 8,
name: 'Wolf Cap',
imageUrl: 'https://i.ibb.co/1f2nWMM/wolf-cap.png',
price: 14
},
{
id: 9,
name: 'Blue Snapback',
imageUrl: 'https://i.ibb.co/X2VJP2W/blue-snapback.png',
price: 16
}
]
},
sneakers:{
id: 2,
title: 'Sneakers',
routeName: 'sneakers',
items: [
{
id: 10,
name: 'Adidas NMD',
imageUrl: 'https://i.ibb.co/0s3pdnc/adidas-nmd.png',
price: 220
},
{
id: 11,
name: 'Adidas Yeezy',
imageUrl: 'https://i.ibb.co/dJbG1cT/yeezy.png',
price: 280
},
{
id: 12,
name: 'Black Converse',
imageUrl: 'https://i.ibb.co/bPmVXyP/black-converse.png',
price: 110
},
{
id: 13,
name: 'Nike White AirForce',
imageUrl: 'https://i.ibb.co/1RcFPk0/white-nike-high-tops.png',
price: 160
},
{
id: 14,
name: 'Nike Red High Tops',
imageUrl: 'https://i.ibb.co/QcvzydB/nikes-red.png',
price: 160
},
{
id: 15,
name: 'Nike Brown High Tops',
imageUrl: 'https://i.ibb.co/fMTV342/nike-brown.png',
price: 160
},
{
id: 16,
name: 'Air Jordan Limited',
imageUrl: 'https://i.ibb.co/w4k6Ws9/nike-funky.png',
price: 190
},
{
id: 17,
name: 'Timberlands',
imageUrl: 'https://i.ibb.co/Mhh6wBg/timberlands.png',
price: 200
}
]
},
jackets:{
id: 3,
title: 'Jackets',
routeName: 'jackets',
items: [
{
id: 18,
name: 'Black Jean Shearling',
imageUrl: 'https://i.ibb.co/XzcwL5s/black-shearling.png',
price: 125
},
{
id: 19,
name: 'Blue Jean Jacket',
imageUrl: 'https://i.ibb.co/mJS6vz0/blue-jean-jacket.png',
price: 90
},
{
id: 20,
name: 'Grey Jean Jacket',
imageUrl: 'https://i.ibb.co/N71k1ML/grey-jean-jacket.png',
price: 90
},
{
id: 21,
name: 'Brown Shearling',
imageUrl: 'https://i.ibb.co/s96FpdP/brown-shearling.png',
price: 165
},
{
id: 22,
name: 'Tan Trench',
imageUrl: 'https://i.ibb.co/M6hHc3F/brown-trench.png',
price: 185
}
]
},
womens:{
id: 4,
title: 'Womens',
routeName: 'womens',
items: [
{
id: 23,
name: 'Blue Tanktop',
imageUrl: 'https://i.ibb.co/7CQVJNm/blue-tank.png',
price: 25
},
{
id: 24,
name: 'Floral Blouse',
imageUrl: 'https://i.ibb.co/4W2DGKm/floral-blouse.png',
price: 20
},
{
id: 25,
name: 'Floral Dress',
imageUrl: 'https://i.ibb.co/KV18Ysr/floral-skirt.png',
price: 80
},
{
id: 26,
name: 'Red Dots Dress',
imageUrl: 'https://i.ibb.co/N3BN1bh/red-polka-dot-dress.png',
price: 80
},
{
id: 27,
name: 'Striped Sweater',
imageUrl: 'https://i.ibb.co/KmSkMbH/striped-sweater.png',
price: 45
},
{
id: 28,
name: 'Yellow Track Suit',
imageUrl: 'https://i.ibb.co/v1cvwNf/yellow-track-suit.png',
price: 135
},
{
id: 29,
name: 'White Blouse',
imageUrl: 'https://i.ibb.co/qBcrsJg/white-vest.png',
price: 20
}
]
},
mens:{
id: 5,
title: 'Mens',
routeName: 'mens',
items: [
{
id: 30,
name: 'Camo Down Vest',
imageUrl: 'https://i.ibb.co/xJS0T3Y/camo-vest.png',
price: 325
},
{
id: 31,
name: 'Floral T-shirt',
imageUrl: 'https://i.ibb.co/qMQ75QZ/floral-shirt.png',
price: 20
},
{
id: 32,
name: 'Black & White Longsleeve',
imageUrl: 'https://i.ibb.co/55z32tw/long-sleeve.png',
price: 25
},
{
id: 33,
name: 'Pink T-shirt',
imageUrl: 'https://i.ibb.co/RvwnBL8/pink-shirt.png',
price: 25
},
{
id: 34,
name: 'Jean Long Sleeve',
imageUrl: 'https://i.ibb.co/VpW4x5t/roll-up-jean-shirt.png',
price: 40
},
{
id: 35,
name: 'Burgundy T-shirt',
imageUrl: 'https://i.ibb.co/mh3VM1f/polka-dot-shirt.png',
price: 25
}
]
}
};
export default SHOP_DATA;
Please provide any pointers or advice on what am I doing wrong? I keep getting the error. It seems that the collection is undefined. I don't understand why. Ideally it should return an object.
You are exporting the component like this
const mapStateToProps = (state,ownProps) => ({
collections: selectCollectionItem(ownProps.match.params.collectionId)(state)
})
export default connect(mapStateToProps)(CollectionPage);
The ownProps.match.params.collectionId might be undefined.
Add this at the top of your file
import { withRouter} from 'react-router-dom'
and export your component like
export default connect(mapStateToProps)(withRouter(CollectionPage));

How to check if arrays have the same object React Native [duplicate]

This question already has answers here:
An efficient way to get the difference between two arrays of objects?
(4 answers)
Closed 3 years ago.
I have two arrays:
const array1 = [
{ id: 1, name: 'John', score: 124 },
{ id: 2, name: 'Angie', score: 80 },
{ id: 3, name: 'Max', score: 56 }
]
const array2 = [
{ id: 5, name: 'Lisa', score: 78 },
{ id: 2, name: 'Angie', score: 80 }
]
JSON.stringify(array1) == JSON.stringify(array2) is not a solution, because arrays have different number of objects.
array1.some(item=> array2.includes(item)) doesnt't work too.
If there a solution?
What i would suggest is since your id is unique , so try the below :
const array1 = [
{ id: 1, name: 'John', score: 124 },
{ id: 2, name: 'Angie', score: 80 },
{ id: 3, name: 'Max', score: 56 }
]
const array2 = [
{ id: 5, name: 'Lisa', score: 78 },
{ id: 2, name: 'Angie', score: 80 }
]
var array2Id = {}
array2.forEach(function(obj){
bIds[obj.id] = obj;
});
// Return all elements in A, unless in B
let check = array1.filter(function(obj){
return !(obj.id in array2Id);
});
console.log(check,'there')
if check is an empty array that means both are same otherwise it will give the different objects.
Hope it helps feel free for doubts
Here is a quick solution, maybe not the most performant.
const array1 = [
{ id: 1, name: 'John', score: 124 },
{ id: 2, name: 'Angie', score: 80 },
{ id: 3, name: 'Max', score: 56 },
];
const array2 = [
{ id: 5, name: 'Lisa', score: 78 },
{ id: 2, name: 'Angie', score: 80 },
];
// Index will have -1 if there is no match, otherwise it'll have
// the index of the first found match in array1.
const index = array1.findIndex(array1Item => {
// This will return the index if found, otherwise -1
const match = array2.findIndex(array2Item => {
// return array1Item.id === array2Item.id;
return JSON.stringify(array1Item) === JSON.stringify(array2Item);
});
return match > -1;
});
Instead of stringifying the entire object, if id is unique, you can just compare id as shown in the commented code.
Be advised this could have a complexity of O(n^2) where n is your array sizes. Big arrays may take a while.
Here is it running on PlayCode. It logs index as 1 because the first match is the second item in array1.

How to get total of numbers in an array from SharePoint list items

I'm creating a spfx web part using Reactjs. I have a function getting an array of items from a SharePoint list that includes a number column of "Hours". I need to get a total for all the hours that have been returned but can't figure out how to calculate that.
I feel like I'm missing something simple but I've run through all kinds of loops and for some reason I can't get it to work. I've verified that I am getting data from the Hours column.
I'll also state the obligatory "I'm new to spfx and react". :) TIA for any help!
private readItem(): void {
this.props.spHttpClient.get(`${this.props.siteUrl}/_api/web/lists/getbytitle('Time Off')/items?$select=Title,Id,Hours`,
SPHttpClient.configurations.v1,
{
headers: {
'Accept': 'application/json;odata=nometadata',
'odata-version': ''
}
}).then((response: SPHttpClientResponse): Promise<ITimeOffItem[]> => {
return response.json();
})
.then((item: ITimeOffItem[]): void => {
console.log(item); //the data is here including Hours
this.setState({
items: item,
hoursTotal: //????How do I get the sum of "Hours" and assign it to a number in state
});
});
}
Create a function to loop through the items and add the hours
function countHours(items) {
if (!items) {
return 0;
}
let total = 0;
for (let i = 0; i < items.length; i++) {
total += items[i].Hours;
}
return total;
}
const item = [
{ Id: 25, Title: "Comp Time", Hours: 6, ID: 25 },
{ Id: 26, Title: "Comp Time", Hours: 5, ID: 26 },
{ Id: 27, Title: "Work from Home", Hours: 3, ID: 27 },
{ Id: 28, Title: "Comp Time", Hours: 7, ID: 28 },
{ Id: 29, Title: "Work from Home", Hours: 8, ID: 29 },
{ Id: 30, Title: "Holiday", Hours: 8, ID: 30 },
{ Id: 31, Title: "Work from Home", Hours: 32, ID: 31 }
];
console.log(countHours(item));
Use it like
this.setState({
items: item,
hoursTotal: countHours(item)
});
you can also use reduce
const item = [
{ Id: 25, Title: "Comp Time", Hours: 6, ID: 25 },
{ Id: 26, Title: "Comp Time", Hours: 5, ID: 26 },
{ Id: 27, Title: "Work from Home", Hours: 3, ID: 27 },
{ Id: 28, Title: "Comp Time", Hours: 7, ID: 28 },
{ Id: 29, Title: "Work from Home", Hours: 8, ID: 29 },
{ Id: 30, Title: "Holiday", Hours: 8, ID: 30 },
{ Id: 31, Title: "Work from Home", Hours: 32, ID: 31 }
];
const sum = item.reduce(function(a, b) { return a + b.Hours; }, 0);
console.log(sum)
It's hard to answer this without knowing what the data structure looks like, but if you are trying to sum an array of numbers you could use reduce.
const hours = [7, 5, 3, 1, 7]
const totalHours = hours.reduce((accumulator, hour) => accumulator + hour)

Is there a way to query MongoDB collection with an embedded array to return the element's offset within the array

Given the following documents in the school collection:
{
_id: 1,
zipcode: "63109",
students: [
{ name: "john", school: 102, age: 10 },
{ name: "jess", school: 102, age: 11 },
{ name: "jeff", school: 108, age: 15 }
]
}
{
_id: 2,
zipcode: "63110",
students: [
{ name: "ajax", school: 100, age: 7 },
{ name: "achilles", school: 100, age: 8 },
]
}
{
_id: 3,
zipcode: "63109",
students: [
{ name: "ajax", school: 100, age: 7 },
{ name: "achilles", school: 100, age: 8 },
]
}
{
_id: 4,
zipcode: "63109",
students: [
{ name: "barney", school: 102, age: 7 },
{ name: "ruth", school: 102, age: 16 },
]
}
Here is the example query in MongoDB docs:
db.schools.find( { zipcode: "63109" },
{ students: { $elemMatch: { school: 108 } } } )
It will return a result like this:
{ "_id" : 1, "students" : [ { "name" : "jeff", "school" : 108, "age" : 15 } ] }
My question is how to get a result like this:
{ "_id" : 1, "students" : [ { "name" : "jeff", "school" : 108, "age" : 15, "offset" : 2 } ] }
As you can see, this result adds a field offset. Has anyone tried this before?
Below is a work around for your problem :
db.schools.find( { zipcode: "63109" },
{ _id : 1 , students : 1 }).forEach( function(doc){
var arr = doc.students;
for( var index = 0 ; index < arr.length ; index++ )
{
if( arr[index].school == 108 )
{
var d = arr[index];
d["offset"] = index;
}
else
{
arr.splice(index, 1);
}
}
});

COUNT in angularJS

So I'm looking to write an AngularJS function that performs a COUNT of how many activities each user has performed.
Ideally: What I'd like to be able to return is (by looking at the data model below):
John Wilson - 2 Deals
Robin Wilson - 3 Deals
Garry Wilson - 3 Deals & 1 Meeting
My question is, how can I conduct a count of each activity using angularJS? What I would like to is create an element that looks like:
<div class="countArea" ng-repeat="activity in selectedUser.activities"
{{activity.count}}
</div>
The code I have so far:
$scope.competitionDetails = {
organisationId: 1,
competitionId: "52eabcf0f3672",
currentUser: 2,
title: "Challenge #1",
description: "Challenge description goes here",
end_date: "2014-03-01",
users: [{
id: 2,
name: "John Wilson",
totalPoints: 40,
activities: [{
id: 6431,
points: 20
}, {
id: 6431,
points: 20
}]
}, {
id: 3,
name: "Robin Wilson",
totalPoints: 60,
activities: [{
id: 6431,
points: 20
}, {
id: 6431,
points: 20
}, {
id: 6431,
points: 20
}]
}, {
id: 1,
name: "Garry Wilson",
totalPoints: 140,
activities: [{
id: 6431,
points: 20
}, {
id: 6431,
points: 20
}, {
id: 6432,
points: 100
}]
}]
};
$scope.activities = [{
id: 6431,
name: "Deal",
points: 20
}, {
id: 6432,
name: "Meeting",
points: 100
}];

Resources