Functional Javascript map with index - javascript

I need a functional approach to solve a very basic problem, the problem with list indexes, let me write an example with React and ramda that shows the need of an index.
const R = require('ramda');
const array = ["foo", "bar", "foobar"];
// Need to put them in a nice html li ?
// this works with a warning that you
// need a unique key to each item.
const renderList = R.map( item => <li>{item}</li> );
// we can solve it like that.
const mapIndexed = R.addIndex(R.map)
const renderListIndexed = mapIndexed((item, id) => <li key={id}>{item}</li>
All of that is cool, but I'm pretty sure the use of an indexed map is not a functional approach, let me know if I'm wrong.

I'm not sure what is Ramda doing since I'm not familiar with React stuff, but if you need an index for elements of your array, you can use basic Array.map function.
const array = ["foo", "bar", "foobar"];
array.map(function(item, index) {
return {
item: item,
id: index
}
});
which will give you an array of objects structured as:
[{ id: 0, item: "foo" }, { id: 1, item: "bar" }, { id: 2, item: "foobar" }]
Hope this helps!

Look at addIndex. There are some very good reasons why Ramda does not include it by default, but this function will mix it in for you.
Try
const renderList = R.addIndex(R.map)( item => <li>{item}</li> );

Related

How to improve nested object arrays lookup

Is there a common known way to chain .map or .filter or .find expressions to accomplish this kind of lookup?
Given and array of objects within an array of objects
customerGroups :
[
{
id: 1,
customers: [{
id: 1, // The same customer may appear in multiple groups
name: 'Jhon'
}],
},
{
id: 2,
customers: [{
id: 2,
name: 'Jhon'
}],
},
{
id: 3,
customers: [{
id: 2,
name: 'Doe'
}],
},
]
In the use case where you have the customer.id and want to find out the customer.name I would like to extract the customers array to use the Array.Find method
const idSearch = 1
const customerName = customers.find(({id})=>id==idSearch).name
So far I been trying with
const customers = customerGroup.find(({ customer }) =>
customer.find(({ id }) =>idSearch === id),
)?.customers
const customerName = customers.find(({id})=>id==idSearch).name
I believe there is a better way to do this but I'm too burnout to figure it out.
I've also tried some shenanigans with the .map to make a new array with all the customers in it but no good results so far.
I could also fetch that array from my Backend but I already have all the customers in memory so that would be an overheat.
There is not one native method that does this, but you could first combine the customer arrays into one with flatMap, and then use find:
const customerGroups = [{id:1,customers:[{id:1,name:'Jhon'}]},{id:2,customers:[{id:2,name:'Jhon'}]},{id:3,customers:[{id:2,name: 'Doe'}]}];
const idSearch = 1;
const allCustomers = customerGroups.flatMap(({customers}) => customers);
const name = allCustomers.find(({id}) => id === idSearch)?.name;
console.log(name);
This approach works because as soon as the inside find loop discovers a result, both the inside and outside loop will terminate, leaving name set as the match which caused the loops to terminate (or as undefined if no match was found).
const d = [{id:1,customers:[{id:1,name:'Jhon'}]},{id:2,customers:[{id:2,name:'Jhon'}]},{id:3,customers:[{id:3,name: 'Doe'}]}]
const idSearch = 1
let name
d.find(j=>j.customers.find(i=>i.id===idSearch && ({name}=i)))
console.log(name)

JavaScript - Creating Object with specific format out of 2 Arrays

I have 2 Arrays.
The Array "people" just holds basic information of some people.
The Array "subscriptions" has a lot of different subscriptions to games.
I want to have have an Array, where I can sort of have an Overview of what game subscriptions each person has. This is not real code I use, I am just trying to get used to JavaScript.
an example Element of an Array called people:
{"number":4251,"name":"Alex","surname":"Scott"}
an example Element of an Array called subscriptions:
{"number":4329,game:"Tetris"}
I want to make a new new Array with the following format:
person: (people[i]), subscriptions: [(subscriptions[j], subscriptions[j+k], ...)]
What I tried:
const array3 = people.map(x => {"person": x , "subscriptions": subscriptions.filter(y => y.number === x.number)});
It get this Error:
SyntaxError: Unexpected token :
How can I insert multiple key value pairs in in these Objects?
This happens because your argument in the map is interperting the { as opening bracket of the method body.
const arr = [{some: 1, object: 2}, {some:3, object:4}]
arr.map((o) => {original: o, cool: 'yes'})
To resolve this you have to explicitly return the object or add additional parenthesis to let the interperter know that this is not the method body:
const arr = [{some: 1, object: 2}, {some:3, object:4}]
const mapped = arr.map((o) => {return {original: o, cool: 'yes'}})
console.log(mapped)
const arr = [{some: 1, object: 2}, {some:3, object:4}]
const mapped = arr.map((o) => ({original: o, cool: 'yes'}))
console.log(mapped)
I hope this helps!
In your original code there is a syntax error, when it comes to the mapping part. Either you go with the long version of this command or, since you directly want to return the element, you can use the short version:
const people = [{"number":4251,"name":"Alex","surname":"Scott"}, {"number": 4329, "name":"Mr", "surname": "Tetri"}]
const subscriptions = [{"number":4329,game:"Tetris"}, {"number":4329, game:"Solitaire"}, {number: 4251, game: "Tetris"}]
// using an anonymous function including full body
const arrayLongVersion = people.map(x => { return {"person": x , "subscriptions": subscriptions.filter(y => y.number === x.number)} });
// using an anonymous function with direct return of elements
const arrayShortVersion = people.map(x => ({person: x , subscriptions: subscriptions.filter(y => y.number === x.number)}))
Here you go
const people = [
{"number":1,"name":"Alex","surname":"Scott"},
{"number":2,"name":"John","surname":"Anderson"},
{"number":3,"name":"Jacob","surname":"Sanderson"},
{"number":4,"name":"Lionel","surname":"Messi"},
{"number":5,"name":"Cristiano","surname":"Ronaldo"}
];
const subscriptions = [
{"number":1,game:"Tetris"},
{"number":2,game:"Super Mario"},
{"number":3,game:"Fortnite"},
{"number":4,game:"FIFA"},
{"number":5,game:"Zelda"}
];
const peopleSubscriptions = people.map(person => {
return {
...person,
subscriptions: subscriptions.filter(sub => sub.number === person.number)
}
});
console.log(peopleSubscriptions);

How to copy a variable arrray from another arrray in javascripts

I have an array as below:
const arr = [
{
title: 's4',
value: '124'
},
{
title: 's2',
value: '121'
},
{
title: 's3',
value: '122'
}
];
and I want to create a new another array copy from the old array same as below:
const arrCopy = [
{
value: '124'
},
{
value: '121'
},
{
value: '122'
}
];
then my code as below:
var arrCopy = [...arr,arr.value]
but it has a problem, so anyone help me, thanks.
Just as in the comment above you can use awesome Javascript functions, in this case, you would like to use the map function of your array to map every item of the array as you like.
const arrayMapped = yourArray.map(item => {
value: item.value
})
Here is another way using Javascript Destructuring, you just ask with properties would you like from the JS Object, in this case, you just like the value property.
const arrayMapped = yourArray.map(( { value } ) => ( { value } ))
How Array.map works
How Object Destructuring works
You can simply use Array.map, as it returns a new array with the required value.
const newArr = arr.map(element => ({ value: element.value }))
console.log(newArr);
For references : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
If you are allowed to import a library. Ramda has a lot of functions to work with arrays.
For your specific question, project would do the job.
import R from "ramda";
R.project(["value"], arr) //return equals arrCopy

Am I updating the React state correctly?

In my React state, I have the state:
this.state = {
users: [{
id: 1,
name: "john",
age: 27
}, {
id: 2,
name: "ed",
age: 18
}, {
id: 3,
name: "mel",
age: 20
}]
}
I am rendering the name correctly. When you click on the name, it should remove the name, which will need an onClick that takes in a function and that returns a removeUser function.
It is my understanding that you do not want to mutate the state in React, but return a new state. So, in my removeUser function, I did:
removeUser(index) {
// Making a new copy of the array
const users = [...this.state.people].splice(index, 1);
this.setState({ users });
}
I bind my method with this.removeUser = this.removeUser.bind(this).
When I tested out my code, I am removing the users as expected. However, when I run my code against a test that my friend wrote, I got a failed test that said: Expected 1 to be 0
That message tells me that I must be mutating the state somehow, but I am not sure how. Am I returning a new array and updating the state correctly? Can someone explain to me how I should update my state correctly in this case?
Here is the full code:
class Group extends Component {
constructor(props) {
super(props);
this.state = {
users: [
{id: 1, name: "john", age: 27},
{id: 2, name: "ed", age: 18},
{id: 3, name: "mel", age: 20}
]
}
this.removeUser = this.removeUser.bind(this);
}
removeUser(index) {
const users = [...this.state.users].splice(index, 1);
this.setState({ users });
}
render() {
const list = this.state.users.map((user, i) => {
return (
<div onClick={() => this.removeUser(i)} key={i}>{user.name}</div>
);
});
return (
<div>
{list}
</div>
);
}
}
you could also change your onClick and pass it the user.id instead of the index. that would allow you to filter the results instead of needing to splice the array.
onClick={() => this.removeUser(user.id)}
removeUser(id) {
const users = this.state.users.filter((user) => user.id !== id);
this.setState({ users });
}
const users = [...this.state.users].splice(index, 1)
looks like it should be removing an item from a collection, which I suppose it technically is. The problem is that users doesn't contain the list of users you want to keep.
Instead, splice has modified your new array in-place and then returned the removed items:
splice docs
Return value
An array containing the deleted elements. If only one element is removed, an array of one element is returned. If no elements are removed, an empty array is returned.
Instead, if you'd like to use splice, create a new array:
const users = [...this.state.users]
and then splice the new array:
users.splice(index, 1)
You'll run into a similar issue if you need to sort an array.
The issue of modifying data in-place is generally frowned upon for react, in favor of immutable references. This is because React uses a lot of direct comparison of objects as a heuristic approach to speed things up. If your function modifies an existing object instance, React will assume the objects haven't changed.
The act of copying to a new object and then operating on the data comes with some tradeoffs. For removing a single item, it's negligible. For removing many items, you may be better served by an alternative method, such as multiple slice calls:
const users = [
...this.state.users.slice(0, index)
...this.state.users.slice(index + 1)
]
However this too is quite verbose.
Another approach is to use an immutable variant of splice:
// this quick example doesn't handle negative start indices
const splice = (start, deleteCount, ...items) => arr => {
const output = []
let i
for (i = 0; i < start && i < arr.length; i++) {
output.push(arr[i])
}
output.push(...items)
for (i += deleteCount; i < arr.length; i++) {
output.push(arr[i])
}
return output
}
const users = splice(index, 1)(this.state.users)
You should use slice() instead of splice() because splice() mutates the original array, but slice() returns a new array. Slice() is a pure function. Pure is better!!
removeUser(index) {
const users = [
...this.state.users.slice(0, index),
...this.state.users.slice(index+1)
];
this.setState({ users });
}
Here is JS Fiddle

Converting flat array to Id and Name object array using Lodash

let states = ["Georgia","California","FL","TX","MA","NJ"];
How do I convert the states array into Id and Name array collection using lodash.
Is there a way to convert the array in below format ( image shown below):
You don't really need lodash to do that.
let states = ["Georgia","California","FL","TX","MA","NJ"];
let result = states.map((item) => {
return {
id: item,
name: item}})
console.log(result)
You do pretty much the same with lodash
import _ from 'lodash';
result = _.map(states, (item) => {
return {
id: item,
name: item}})
let states = ["Georgia","California","FL","TX","MA","NJ"];
const newObj = [];
_.each(states, state => newObj.push({ id: state, name: state }));
console.log(newObj);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
_.each performs a function on each item of an Array. With that you can create a new object for each state and push that into a new Array. Note: this could also be accomplished with JavaScript's built in .map.
----- UPDATE -----
Why did I make this complicated many years ago?
const states = ["Georgia","California","FL","TX","MA","NJ"];
const newObj = states.map(state => ({ id: state, name: state }));
console.log(newObj);
No need to use lodash, just map through the array and return a new object for each item in the array.

Categories

Resources