.map & .slice a unique value from data set - javascript

I have a big list of player info. I am pulling the players onto the front end. I have managed to map them and slice them to bring back the first 11 players. Please see below code for this. I now only want to bring back the players from a unique position (value).
render() {
const { players } = this.props;
const { primaryPositionNumber, image, fullName, playerId } = players;
const playerPositions = this.props.players.slice(0, 11).map(function(player) {
return (
<Chip className="player" data-position={player.primaryPositionNumber}
avatar={<Avatar alt={player.fullName} src={`${player.image}.png`}/>}
label={player.fullName}
key={player.playerId}
/>
);
});
return
<div>
{playerPositions}
</div>
}
I want to bring back a maximum of 11 players but only have one player from each {player.primaryPositionNumber} value. I will therefore end up with 11 players all in a different position. I am using es6, lodash and react if these can be useful here?

const players = [
{
number: 1,
name: 'Timmy'
},
{
number: 2,
name: 'Bob'
},
{
number: 2,
name: 'Rob'
},
{
number: 1,
name: 'Ryan'
}
];
const playerNumbers = [1,2];
const filteredPlayers = playerNumbers.map(n => players.find(f => n === f.number));
console.log(filteredPlayers);
As an example, I assume you just need to filter the players by the playerNumbers. players.find will return the first encountered.

Related

How do I update a specific key in an array of keys using React

I've got a counter array, containing 3 objects.
const [counter, setCounter] = useState([
{ id: 0, count: [] },
{ id: 1, count: [] },
{ id: 2, count: [] }
])
Theres then 3 buttons that when pressed call the following function.
const update = (i, text) => {
setCounter(currCount =>
currCount.id === i
? { id: i, count: [...counter[i].count, text] }
: currCount
);
};
The buttons pass "i" which is 0,1,2 corresponding to the 3 object ids and "text" which is the error text.
The function should update the specific object from the array adding the new error text to that id's count array.
I cant seem to get this to work though, it keeps returning undefined.
Any help is appreiciated.
The useState dispatch function (setCounter in your case) replaces the whole state with the value it is provided with.
In your example, you need to recreate the whole array like so:
const update = (i, text) => {
setCounter(currCounter =>
[
...currCounter.filter(count => count.id !== i), // the old counter state without the one we want to change
{ id: i, count: [...currCounter[i].count, text] }
]
);
};

React-Native | Generate a non-repeating (name) generator in React-Native

Hi everyone!
I have a question that I hope you can help me with.
I just started with React Native and I'm working on a simple name generator.
I have an array with different names in it.
When I click on the button, a random number is generated. This number is associated with the array's list of names.
This all works, but I'm getting duplicate names. I would like to go through the whole list without there being a duplicate name. When all names have been passed, the list starts again.
I was thinking of making a separate array that keeps track of the numbers that have passed. And then exclude those numbers. But I'm not sure how to add this and if this is the right way.
See below my code.
Apologies if this is a bit messy or cumbersome.
import React, { useState } from "react";
import { StyleSheet, Text, View, Button } from "react-native";
export default function GirlScreen() {
const RandomNumber = (min, max) => {
return Math.floor(Math.random() * (max - min + 1) + min);
};
const [count, setCount] = useState(0);
const onPress = () => {
setCount(RandomNumber(1, 100));
};
const random = RandomNumber(1, 5);
var differentNames = {
namesContainer: {
names: [
{ name: "(1) Sophie", id: 1 },
{ name: "(2) Emma", id: 2 },
{ name: "(3) Lisa", id: 3 },
{ name: "(4) Esmée", id: 4 },
{ name: "(5) Zoe", id: 5 },
],
},
};
function findLinkByName(random) {
for (const item of differentNames.namesContainer.names) {
if (item.id === random) {
return item.name;
}
}
}
return (
<View style={styles.countContainer}>
<Text style={styles.name}>{findLinkByName(random)}</Text>
<Button onPress={onPress} title="Next Name" />
</View>
);
}
const styles = StyleSheet.create({
countContainer: {
flex: 1,
alignItems: "center",
justifyContent: "center",
},
name: {
color: "black",
fontSize: 30,
},
});
You could keep track of two states. One holds already selectedNames and the other one holds still availableNames as follows.
const [selectedNames, setSelectedNames] = useState([])
const [availableNames, setAvailableNames] = useState([
{ name: "(1) Sophie", id: 1 },
{ name: "(2) Emma", id: 2 },
{ name: "(3) Lisa", id: 3 },
{ name: "(4) Esmée", id: 4 },
{ name: "(5) Zoe", id: 5 },
])
Then, we choose a random number between 0 and the length of avialableNames which represents the index we want to pick from avialableNames.
const random = RandomNumber(0, availableNames.length - 1);
Then, your onPress function looks as follows.
const onPress = () => {
setAvailableNames(availableNames.filter(n => n !== availableNames[random]))
setSelectedNames([...selectedNames, availableNames[random]])
};
We add the new randomly picked name to selectedNames and remove it from availableNames at the same time.
Your findLinkByName function could look as follows.
function findLinkByName(random) {
if (availableNames.length === 0) {
setAvailableNames(selectedNames.sort((a, b) => a.id - b.id))
setSelectedNames([])
return availableNames[0]
}
return availableNames[random].name
}
As long as there are names in availableNames, that is its length is not equal to 0, we just pick it and return its name. If all avialable names have been selected, we reset the states, sort the selectedNames by their id prop and return the first name of the list again.
Here is a working snack.
if I understand you want to select without return
let arr1 = ['a', 'b', 'c'];
let value;
Option1: Copy the array to temp array
let arr2 = [...arr1];
let random_index = Math.floor(Math.random() * arr2.length);
value = arr2[random_index];
arr2 = arr2.filter((val, index) => index !== random_index);
if (arr2.length === 0)
arr2 = [...arr1];
Option2: Save the indexes of the array
let arr2 = Array.from(Array(arr1.length).keys());
let random_index = Math.floor(Math.random() * arr2.length);
value = arr1[arr2[random_index]];
arr2 = arr2.filter((val, index) => index !== random_index);
if (arr2.length === 0)
arr2 = Array.from(Array(arr1.length).keys());
Option 1: Quick and Easy
Create an object in state to track the used name IDs.
const [usedIds, setUsedIds] = useState([]);
Then update the findLinkByName function to use this array. You should also invoke the random number generator inside the function.
function findLinkByName() {
// clear array if full
if(usedIds.length === differentNames.namesContainer.names.length) {
setUsedIds([]);
}
// find unique ID
let randomId;
do {
randomId = RandomNumber(1,5);
} while(usedIds.includes(randomId));
// add used ID to array
setUsedIds(prev => [...prev, randomId]);
// return random name
return differentNames.namesContainer.names.find(n => n.id === randomId);
}
Option 2: Move Names to State
You can also simply append a used property to each name object in the name container and change it to be stored in state so that we can mutate it. The ugliest part of this is that the names are kept 3 levels deep in the object. If that can be lifted up, then the following statements could be much shorter.
const [names, setNames] = useState({
namesContainer: {
names: [
{ name: "(1) Sophie", id: 1, used: false },
{ name: "(2) Emma", id: 2, used: false },
{ name: "(3) Lisa", id: 3, used: false },
{ name: "(4) Esmée", id: 4, used: false },
{ name: "(5) Zoe", id: 5, used: false },
],
},
});
I'd also recommend using const and let over var for various reasons 😉
Then your findLinkByName function can be updated to work much more efficiently like this:
function findLinkByName() {
// clear array if full
if(names.namesContainer.names.filter(n => !n.used).length === 0) {
let newNames = {...names};
newNames.namesContainer.names.map(n => {...n, used: false});
setNames(newNames);
}
// find random ID
const unusedNames = names.filter(n => !n.unsed);
const randId = Math.floor(Math.random() * unusedNames.length);
// update state
let newNames = {...names};
newNames.namesContainer.names.map(n => {
return (n.id === randId) ? {...n, used: true} : {...n}
});
setNames(newNames);
// return random name
return names.namesContainer.names.find(n => n.id === randId);
}

React.js search filtration on the array of objects

I'm facing a problem with the search. It is a front-end search rather than a remote search, I'm using react.js because it is a requirement in the problem and created a component named App. My task is to display and highlight the matching parts according to the type value.
I will appreciate it. If you provide me a good solution for this.
Let me tell you the whole scenario. I'm dividing this problem into 3 parts.
Part 1: What is the shape of the data?
The shape of the data is this:
src/data.js:
export default [
{
id: 1,
name: 'Wordpress',
list: [
{
id: 1,
name: 'Best Mobile App Builder',
slug: '/'
},
{
id: 2,
name: 'Best Wordpress Themes',
slug: '/'
},
{
id: 3,
name: 'Best Website Creator',
slug: '/'
},
{
id: 4,
name: 'Best Wordpress Builder',
slug: '/'
}
]
},
{
id: 2,
name: 'SaaS',
list: [
{
id: 1,
name: 'Appointment Scheduling Software',
slug: '/'
},
{
id: 2,
name: 'Design Services',
slug: '/'
},
{
id: 3,
name: 'Online Cloud Storage',
slug: '/'
},
{
id: 4,
name: 'Remote PC Access',
slug: '/'
}
]
},
];
Note:
Basically this is my filter function.
src/filter.js:
import _ from 'lodash';
import match from 'autosuggest-highlight/match';
import parse from 'autosuggest-highlight/parse';
/**
* Returns the new filtered array with highlighted parts.
* #param data {Array<Object>} - The collection to iterate over.
* #param inputValue {string} - The input value.
* #return {Array} - Returns the new filtered array.
*/
export const filterByNames = (data, inputValue) => {
// Create a dynamic regex expression object with ignore case sensitivity
const re = new RegExp(_.escapeRegExp(inputValue), 'i');
const results = data.filter((object) => {
if (re.test(object.name)) {
return true;
} else {
return object.list.some((item) => {
if (re.test(item.name)) {
// Calculates the characters to highlight in text based on query
const matches = match(item.name, inputValue);
// Breaks the given text to parts based on matches.
// After that create a new property named `parts` and assign an array to it.
item['parts'] = parse(item.name, matches);
return true;
} else {
return false;
}
});
}
});
return results;
};
The search is working fine but facing 2 major issues.
When the above match of the name property occurs, then it stops and does not go much deeper. The same thing is happening with the nested list name property.
When the filtration happens behind the scenes we're mutating the original data by adding a new property named parts which contains highlighted parts and it is an array of objects. But I don't want to mutate the original data instead wants to return the new filtered array which contains parts property.
See this.
WORKING DEMO :
Part 2: Which third-party libraries I'm using for filter and highlighting?
lodash string function escapeRegExp for escapes the RegExp
special characters.
autosuggest-highlight match function to calculates the
characters to highlight in text based on the query.
After that, from the same library parse function help us to break the given text to parts based on matches. In the end, it will
return an array of objects with the match string and highlight
boolean flag. So it's easy for us to bold the highlighted parts on the UI.
Part 3: App component
import React, { useState } from 'react';
import { filterByNames } from './filter';
import data from './data';
/**
* Return the JSX for the List
* #param data {Array<Object>} - The collection to iterate over.
* #return {null|*} - Returns the JSX or null.
*/
const renderList = (data) => {
if (Array.isArray(data) && data.length > 0) {
return data.map((object) => {
return (
<div key={object.id}>
<h1>{object.name}</h1>
<ul className="list">
{object.list.map((item) => {
return (
<li key={item.id}>
{item.parts ? (
<a href={item.slug}>
{item.parts.map((part, index) => (
<span
key={index}
style={{ fontWeight: part.highlight ? 700 : 400 }}
>
{part.text}
</span>
))}
</a>
) : (
<a href={item.slug}>{item.name}</a>
)}
</li>
)
})}
</ul>
</div>
)
})
} else {
return null
}
};
// Main App Component
const App = () => {
const [value, setValue] = useState('');
const onChangeHandler = (event) => {
const { target } = event;
const val = target.value;
setValue(val);
};
const results = !value ? data : filterByNames(data, value);
return (
<div className="demo">
<input type="text" value={value} onChange={onChangeHandler}/>
<div className="demo-result">
{ renderList(results) }
</div>
</div>
);
};
export default App;
Here is the revised code.
export const filterByNames = (data, inputValue) => {
// Create a dynamic regex expression object with ignore case sensitivity
const re = new RegExp(_.escapeRegExp(inputValue), "i");
const clonedData = _.cloneDeep(data);
const results = clonedData.filter((object) => {
return object.list.filter((item) => {
if (re.test(item.name)) {
// Calculates the characters to highlight in text based on query
const matches = match(item.name, inputValue);
// Breaks the given text to parts based on matches.
// After that create a new property named `parts` and assign an array to it.
item["parts"] = parse(item.name, matches);
return true;
} else {
return false;
}
}).length > 0 || re.test(object.name);
});
return results;
};
Forked link here.
https://codesandbox.io/s/search-frontend-forked-e3z55
Here is the code having solved both
export const filterByNames = (data, inputValue) => {
// Create a dynamic regex expression object with ignore case sensitivity
const re = new RegExp(_.escapeRegExp(inputValue), "i");
// since we cannot directly mutate the data object, why not copy it here ? (or if the data is bigger and copying is also not an option then consider using two arrays of data, one for the mutation and one default maybe)
let data_ = JSON.parse(JSON.stringify(data));
// filter and return the newer copy of the object.
const results = data_.filter((object) => {
// since we need the highlighting in both cases, on top level, or even in nested level so create separate function for that.
let highLightEm = (list) => {
return object.list.some((item) => {
if (re.test(item.name)) {
// Calculates the characters to highlight in text based on query
const matches = match(item.name, inputValue);
// Breaks the given text to parts based on matches.
// After that create a new property named `parts` and assign an array to it.
item["parts"] = parse(item.name, matches);
return true;
} else {
return false;
}
});
};
if (re.test(object.name)) {
// check for the highlighting in the inner name
highLightEm(object);
return true;
} else {
return highLightEm(object);
}
});
return results;
};
https://codesandbox.io/s/search-frontend-forked-kxui9?file=/src/filter.js

Merge Data from different Queries without duplicates

I am getting data from three different queries via Api. I want data to be merged without the duplicate data.
This is my function where i am merging the data:
getStaffCount(data) {
if (data == null || data.results === null )
return [];
else
return data.results.StaffCount.map(m => ({ Name: m.Name, Accounts: m.Accounts })).
concat(data.results.RepProviderAccount.map(m => ({ Name: m.Name, Accnt: m.Accnt }))).
concat( data.results.ProviderAccount.map(m => ({ Name: m.Name, Account: m.Account })));
}
This is my table:
<PowerTable Data={{ rows: this.getStaffCount(this.props.GridData) }} rowsPerPage={5} orderBy="Name" order="asc" >
<PowerColumn id='Name' columnName='Name' numeric={false} disablePadding={false} label='Profile Name' width={100}>
</PowerColumn>
<PowerColumn id='Accounts' columnName='Accounts' numeric={false} disablePadding={false} label='Staff Accounts' width={100}>
</PowerColumn>
<PowerColumn id='Account' columnName='Account' numeric={false} disablePadding={false} label='Provider Account' width={100} >
</PowerColumn>
<PowerColumn id='Accnt' columnName='Accnt' numeric={false} disablePadding={false} label='Rep Provider Account' width={100} >
</PowerColumn>
</PowerTable>
So in the above image same Profile Name(CNX MSL Platform) is coming twice. So is there any way i can merged those rows?
I want the Output in this way:
Profile Name Staff Provider Rep Provider
Cnx MSl Platform 2 1
Cnx Specilaity sales Platform 7 22
Data:
As an object
if the data is an object the easy way to do that is the spread opperator
const combinedData = {
...dataSrc1,
...dataSrc2,
...dataSrc3,
}
All matching keys will be overwritten by the previous
As an array
It's a bit more complex. Assuming your object has a unique id (or any value to identify 2 as the same item) you can use a Set since they can only have unique values.
const array = [
...dataSrc1,
...dataSrc2,
...dataSrc3,
]
const unique = [...new Set(array.map(item => item.id))];
Your answer to my question about what the data looks like and how to group them didn't make any sense, neither did you answer Joe just showed the json data and tell him where the data comes from instead of what it is.
So I assume you group by Name and Account is ignored. You can group them in the following way:
const data = {
results: {
StaffCount: [
{
Name: 'a',
Accounts: 2,
},
{
Name: 'b',
Accounts: 20,
},
],
RepProviderAccount: [
{
Name: 'a',
Accnt: 3,
},
],
ProviderAccount: [
{
Name: 'a',
Account: 1,
},
],
},
};
const grouped = [
...data.results.StaffCount,
...data.results.RepProviderAccount,
...data.results.ProviderAccount,
].reduce((result, item) => {
const {
Name,
Account = 0,
Accounts = 0,
Accnt = 0,
} = item;
const existing = result.get(item.Name) || {
Name,
Account: 0,
Accounts: 0,
Accnt: 0,
};
existing.Account += Account;
existing.Accounts += Accounts;
existing.Accnt += Accnt;
return result.set(Name, existing);
}, new Map());
console.log([...grouped.values()]);
In case this doesn't work for you can you please update your question and provide code as in my answer with the expected input and output? You can respond to this answer and I'll have a look at your question again.
This may actually be an xy problem, you are fetching 3 data sources and then trying to group and sum them but maybe you can just get 1 data source and try salesforce to group and sum them in the query. I don't know enough about salesforce but maybe you can ask another question tagging it with soql if it's possible to just get the data grouped and summed.

Find matching item across multiple store arrays in VueX

Currently when I want to find single item in an array that is in store I use this:
this.matched = this.$store.state.itemlist.find(itemId=> {
return itemId.id == "someid";
});
Lets says I want to go over multiple arrays to find the matching item given provided ID? Like i have itemlist1 itemlist2 itemgetter()... Some of the arrays are getters ( but I think it doesnt change much). So basically I want to search over different state and getter items in this component instead of searching over one as in example above.
if you just want to find if its exist in one the arrays you can simply write function like this
function find(search,...arrs){
return arrs.flat(1).find(item => item == search)
}
this function merge all arrays to one long array and search in it
example of usage
let a=[1,2,3,4]
let b=[5,6,7,8]
let c=[9,10,11,12]
let i=find(6,a,b)
console.log(i)
Using one object to group all the arrays, so that will be possible to iterate over them. The idea is something like below:
const store = new Vuex.Store({
state: {
itemsGroupArrays: {
items1: [{ id: 1, text: "item1 - 1" }, { id: 2, text: "item1 - 2" }],
items2: [{ id: 3, text: "item2 - 1" }, { id: 4, text: "item2 - 2" }]
}
},
getters: {
getItemByIdFromStateGroupArrays: state => (id) => {
let returnedItem = null;
Object.values(state.itemsGroupArrays).forEach((itemStateArray) => {
if (itemStateArray.some(item => item.id === id)) {
returnedItem = itemStateArray.find(item => item.id === id);
}
})
return returnedItem;
}
}
});

Categories