transform an array of objects with map( ) - javascript

I can't understand how the map () method works because all the examples are with numbers and to understand I need an example with something more specific.
so I made this
I have an array of objects:
let people = [
{
id: 1,
name: 'jhon',
last_name: 'wilson'
},
{
id: 2,
name: 'maria',
last_name: 'anyway'
},
{
id: 3,
name: 'lastOne',
last_name: 'example'
}
];
I want to understand how with people.map(); i can change the idk, name?? of the 2nd element.
this is how i think map() work:
people.map(() => {
people[1].name = prompt()
// At this point i don't know how continue
})
I'm studying on my own, so I will be very grateful to you :)

The .map() function will go through the entire array, and on each step of that process it will take the current item that we are looking at and will pass it as a parameter into the function. You can then do whatever you want to that item, and whatever you return from your function will replace what is in that position in the array.
Say for example, with the array you gave in your question, we wanted to remove the name and last_name properties, and combine them into a full_name property. We can do the following:
let people = [
{
id: 1,
name: 'jhon',
last_name: 'wilson'
},
{
id: 2,
name: 'maria',
last_name: 'anyway'
},
id: 3,
name: 'lastOne',
last_name: 'example'
}
];
people = people.map((person) => {
return {
id: person.id,
full_name: `${person.name} ${person.last_name}`
}
});
After this code runs, our people array would look like this:
[
{
id: 1,
full_name: 'jhon wilson'
},
{
id: 2,
full_name: 'maria anyway'
},
id: 3,
name: 'lastOne example'
}
];
You can think of it as doing something very similar to this:
function transformPerson(person) {
return {
id: person.id,
full_name: `${person.name} ${person.last_name}`
}
}
let newPeople = [];
for (let i = 0; i < people.length; i++) {
newPeople[i] = transformPerson(people[i])
}
people = newPeople;

Array.map() takes in a function as a parameter, passes each item of the array into the function, and returns an array of the result.
For example, if I wanted to multiply each of the items in the array by 2:
const x = [1, 2, 3, 4, 5]
const y = x.map(v => v * 2) // result: [2, 4, 6, 8, 10]
Note: Array.map does not affect the original array; it creates a new array of the results.

You could change your code to
let people = [{id:1,name:'john',last_name:'wilson'},{id:2,name:'maria',last_name:'anyway'},{id:3,name:'lastOne',last_name:'example'}];
people = people.map((p,i) =>({...p,name: i===1?prompt("New name"):p.name}))
console.log(people);
This will prompt the user only for a new name when i===1. The expression will create a new array that will be stored under the variable name people again. If you wanted people to remain unchanged you could assign the return value of the people.map()-call to a different variable (or constant).

Related

Functional Programming exercise I can't figure out

I've been looking at a problem for hours and failing to find a solution. I'm given an array of customer objects.
In each customer object is an array of friends.
In the array of friends is an object for each friend, containing some data, including a name key/value pair.
What I'm trying to solve for: I'm given this customers array and a customer's name. I need to create a function to find if this customer name is in any other customer's friend lists, and if so, return an array of those customer's names.
Below is a customer list. And as an example, one of the customers is Olga Newton. What the code should be doing is seeing that Olga Newton is a customer and is also in the friends lists of Regina and Jay, and should be returning an array of Regina and Jay.
I thought I could do this simply with a filter function, but because the friends list is an array with more objects, this is adding level of complexity for me I can't figure out.
Below is a customer array. The out put should be
['Regina', 'Jay']
and what I've gotten has either been
[{fullCustomerObj1}, {fullCustomerObj2}]
or
[ ]
What am I missing?
Here is the customer array:
var customers = [{
name: "Olga Newton",
age: 43,
balance: "$3,400",
friends: [{
id: 0,
name: "Justice Lara"
}, {
id: 1,
name: "Duke Patrick"
}, {
id: 2,
name: "Herring Hull"
}, {
id: 3,
name: "Johnnie Berg"
}]
}, {
name: "Regina",
age: 53,
balance: "$4,000",
friends: [{
id: 0,
name: "Cheryl Kent"
}, {
id: 1,
name: "Cynthia Wells"
}, {
id: 2,
name: "Gutierrez Waters"
}, {
id: 3,
name: "Olga Newton"
}]
}, {
name: "Jay",
age: 28,
balance: "$3,000",
friends: [{
id: 0,
name: "Cross Barnett"
}, {
id: 1,
name: "Raquel Haney"
}, {
id: 2,
name: "Olga Newton"
}, {
id: 3,
name: "Shelly Walton"
}]
}];
Use filter and map, please.
function friends(c, name){
return c.filter((a) => {
return a.friends.map(b => b.name).includes(name)
}).map(a => a.name);
}
console.log(friends(customers, "Olga Newton"));
// ['Regina', 'Jay']
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
We look to an array (friends[]) inside anther (customers[]), So used two for loops, the first determine witch customer will look for his friends, and the second the array will search inside, then set if statement if the cust name is inside friends[]: adding the customer name to customerFriends[] array, At the end return the customerFriends[].
let cust = "Olga Newton"; // Get the customer name who you look for his friends.
const findFriend = (cust, arrs) => { // Create findFriend function.
let customerFriends = []; // Create an array to set the result to it.
for (let i = 0; i < arrs.length; i++) { // For each Customer.
for (const arr of arrs[i].friends) { // For each Friend.
if (arr.name === cust) { // Use Strict equality to find Customer name in friends[].
customerFriends.push(arrs[i].name); // Add the customer name to the customerFriends[].
}
}
}
return customerFriends;// Return the final results.
}
console.log(findFriend(cust, customers)); // Call the function.

How can i display more than one array elements that satisfy a condition?

How can I display multiple values of an array to the console that match the condition (e.g: === "McDonalds")?
I only managed to display one item. But I don't know how i can display all the value of my array.
public products: product[] = [
{ id: 1, name: "McFlurry", price: 2, enseigne:"McDonalds" },
{ id: 2, name: "Potatoes", price: 3, enseigne:"McDonalds" },
{ id: 3, name: "BigMac", price: 4, enseigne:"KFC" },
{ id: 4, name: "Nuggets", price: 3, enseigne:"KFC" }
];
searchEnseigne(){
let server = this.products.find(x => x.enseigne === "McDonalds");
console.log(server);
}
let server = this.products.filter(x => x.enseigne === "McDonalds");
console.log(server);
Use filter instead of find:
The filter() method creates a new array with all elements that pass the test. While The find() method returns the value of the first element
searchEnseigne(){
let server = this.products.filter(x => x.enseigne === "McDonalds");
console.log(server);
}

How to prevent lodash mapKeys from reordering my array?

I'm using lodash mapKeys to take my array of objects and convert it to a mapped object using the id property. That's simple enough, but the problem is that it's sorting the new object by id.
For example if I had three objects in my array:
let myArray = [
{
id: 3,
name: 'Number Three'
},
{
id: 1,
name: 'Number One'
},
{
id: 2,
name: 'Number Two'
}
];
Then I map the keys by id:
_.mapKeys(myArray, 'id')
It returns the following:
{
1: {
id: 1,
name: 'Number One'
},
2: {
id: 2,
name: 'Number Two'
},
3: {
id: 3,
name: 'Number Three'
}
}
My server returns the array in a specific order, so I would like the objects to remain the same, so that when I loop over the object properties, they are in the correct order.
Is that possible with this method? If not, is there a possible alternative to achieve the results?
Use a Map because each item has a custom key (like objects), but the order of insertion will be the order of iteration (like arrays):
const myArray = [
{
id: 3,
name: 'Number Three'
},
{
id: 1,
name: 'Number One'
},
{
id: 2,
name: 'Number Two'
}
];
const map = myArray.reduce((map, item) => map.set(item.id, item), new Map());
map.forEach((item) => console.log(item));
As pointed out in the comments, looping over an object doesn't guarantee order. If you want an ordered list, you need an array.
However, you could apply the iterator pattern. In this pattern, it's up to you to decide what “next” element is. So, you could have a set with the objects (in order to get them in constant time) and an array to store the order. To iterate, you'd use the iterator.
This code could be used as example.
Hope it helps.
let myArray = [{
id: 3,
name: 'Number Three'
}, {
id: 1,
name: 'Number One'
}, {
id: 2,
name: 'Number Two'
}];
let myIterator = ((arr) => {
let mySet = _.mapKeys(arr, 'id'),
index = 0,
myOrder = _.map(arr, _.property('id'));
return {
getObjById: (id) => mySet[id],
next: () => mySet[myOrder[index++]],
hasNext: () => index < myOrder.length
};
})(myArray);
// Access elements by id in constant time.
console.log(myIterator.getObjById(1));
// Preserve the order that you got from your server.
while (myIterator.hasNext()) {
console.log(myIterator.next());
}
<script src="https://cdn.jsdelivr.net/lodash/4.16.6/lodash.min.js"></script>
Like mentioned in the comments, the best would be to keep the object references both in an array to keep the order and in a hash to ease updating.
Backbone's collection (source) works like this. It keeps objects in an array (models), but automatically updates a hash (_byId) when adding and removing models (objects) or when a model's id changes.
Here's a simple implementation of the concept. You could make your own implementation or check for a collection lib.
// a little setup
var array = [];
var hash = {};
var addObject = function addObject(obj) {
hash[obj.id] = obj;
array.push(obj);
}
// Create/insert the objects once
addObject({ id: 3, name: 'Number Three' });
addObject({ id: 1, name: 'Number One' });
addObject({ id: 2, name: 'Number Two' });
// Easy access by id
console.log("by id with hash", hash['1']);
// updating is persistent with the object in the array
hash['1'].name += " test";
// keeps the original ordering
for (var i = 0; i < 3; ++i) {
console.log("iterating", i, array[i]);
}

Comparing partial objects in ramda.js

There is an equals function in Ramdajs which is totally awesome, it will provide the following:
// (1) true
R.equals({ id: 3}, { id: 3})
// (2) true
R.equals({ id: 3, name: 'freddy'}, { id: 3, name: 'freddy'})
// (3) false
R.equals({ id: 3, name: 'freddy'}, { id: 3, name: 'freddy', additional: 'item'});
How would I go about enhancing this function, or in some other way produce a true result for number 3
I would like to ignore all the properties of the rValue not present in the lValue, but faithfully compare the rest. I would prefer the recursive nature of equals remain intact - if that's possible.
I made a simple fiddle that shows the results above.
There's a constraint on equals in order to play nicely with the Fantasy Land spec that requires the symmetry of equals(a, b) === equals(b, a) to hold, so to satisfy your case we'll need to get the objects into some equivalent shape for comparison.
We can achieve this by creating a new version of the second object that has had all properties removed that don't exist in the first object.
const intersectObj = (a, b) => pick(keys(a), b)
// or if you prefer the point-free edition
const intersectObj_ = useWith(pick, [keys, identity])
const a = { id: 3, name: 'freddy' },
b = { id: 3, name: 'freddy', additional: 'item'}
intersectObj(a, b) // {"id": 3, "name": "freddy"}
Using this, we can now compare both objects according to the properties that exist in the first object a.
const partialEq = (a, b) => equals(a, intersectObj(a, b))
// again, if you prefer it point-free
const partialEq_ = converge(equals, [identity, intersectObj])
partialEq({ id: 3, person: { name: 'freddy' } },
{ id: 3, person: { name: 'freddy' }, additional: 'item'})
//=> true
partialEq({ id: 3, person: { name: 'freddy' } },
{ id: 3, person: { age: 15 }, additional: 'item'})
//=> false
Use whereEq
From the docs: "Takes a spec object and a test object; returns true if the test satisfies the spec, false otherwise."
whereEq({ id: 3, name: 'freddy' }, { id: 3, name: 'freddy', additional: 'item' })
The other way around is to develop your own version. It boils down to:
if (is object):
check all keys - recursive
otherwise:
compare using `equals`
This is recursive point-free version that handles deep objects, arrays and non-object values.
const { equals, identity, ifElse, is, mapObjIndexed, useWith, where } = R
const partialEquals = ifElse(
is(Object),
useWith(where, [
mapObjIndexed(x => partialEquals(x)),
identity,
]),
equals,
)
console.log(partialEquals({ id: 3 }, { id: 3 }))
console.log(partialEquals({ id: 3, name: 'freddy' }, { id: 3, name: 'freddy' }))
console.log(partialEquals({ id: 3, name: 'freddy' }, { id: 3, name: 'freddy', additional: 'item' }))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>
I haven't used Ramda.js before so if there's something wrong in my answer please be free to point out.
I learned the source code of Ramda.js
In src/equals.js, is where the function you use is defined.
var _curry2 = require('./internal/_curry2');
var _equals = require('./internal/_equals');
module.exports = _curry2(function equals(a, b) {
return _equals(a, b, [], []);
});
So it simply put the function equals (internally, called _equals) into the "curry".
So let's check out the internal _equals function, it did check the length in the line 84~86:
if (keysA.length !== keys(b).length) {
return false;
}
Just comment these lines it will be true as you wish.
You can 1) just comment these 3 lines in the distributed version of Ramda, or 2) you can add your own partialEquals function to it then re-build and create your version of Ramda (which is more recommended, from my point of view). If you need any help about that, don't hesitate to discuss with me. :)
This can also be accomplished by whereEq
R.findIndex(R.whereEq({id:3}))([{id:9}{id:8}{id:3}{id:7}])

How to flatten an array before applying arrayOf('schema') in normalizrJS

Suppose a user has favorited many cats and the api endpoint gives a list of cats when asked for user's favorites:
const catsList = [{id:1,name:"catA"},{id:2,name:"catB"},{id:3,name:"catC"}];
// ex: GET http://app.com/api/favorites => results in following json
{favorites:[{id:2,name:"catB"},{id:3,name:"catC"}]}
// the result is the list of cats
//and normalizr schema
const cats = new Schema('cats');
const favorites = new Schema('favorites');
now my question is, how will I normalize these entities so that I have the following result,
entities.favorites=[2,3]; //2 and 3 are cats
entities.cats=[{id:1,name:"a"},{id:2,name:"b"},{id:3,name:"c"}];
how will I accomplish this with normalizr ?
Something like
const cats = new Schema('cats');
const response = {
favorites: [{
id: 2,
name: "catB"
}, {
id: 3,
name: "catC"
}]
}
const normalized = normalize(response, {
favorites: arrayOf(cats)
})
The normalized value will be like
{
result: {
favorites: [2, 3]
},
entities: {
cats: {
2: {
id: 2,
name: "catB"
}, {
id: 3,
name: "catC"
}
}
}
}
It’s actually pretty important that normalized.entities is an object rather than an array—otherwise it would be slow to query by ID.
At this point you are free to transform this representation anyhow you like.

Categories