Angular compare array and update one of it - javascript

I have this two sets of array, I need to compare them and if the object from 2th array found on 1st array, I need to update the status on 1st array to true. On angular how to massage the data, thanks!
Below are sample of array,
arr1 = [
{id: 1, status: false},
{id: 2, status: false},
{id: 3, status: false}
];
arr2 = [
{id: 4},
{id: 5},
{id: 2}
];

Even though you have not provided any (faulty) code from yourself, I understand that it sometimes can be difficult to even begin with such a thing.
In this case, you should find a beginning point. You can use the source array (arr1) as starting point, or arr2. If arr2 is tiny, it's quicker to use this as starting point. The bigger arr2 gets, the more you might want to start with arr1, because that solution is more readable (subjective).
With arr2 as starting point, you can use the forEach method, in combination with find:
arr2.forEach((item) => {
const target = arr1.find(({ id }) => item.id === id);
if (target) {
target.status = true;
}
});
With the arr1 as starting point, you can use the map method. This will make a new array in place, which is usually a good thing to do, considering angular's change detection system:
arr1 = arr1.map((item) => ({
...item,
status: arr2.some(({ id }) => item.id === id)
});
This will also create a new object reference. Depending on your data set, performance might be an issue, but this will only be noticable if you are processing +10.000 objects (depending on the complexity of your objects).
If you do not want a status: true be overwritten to false on arr1, you need to add an extra condition:
arr1 = arr1.map((item) => ({
...item,
status: item.status || arr2.some(({ id }) => item.id === id)
});

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)

JS - How to add key:value pairs from objects nested in arrays to other objects nested in another array

I know it has been countlessly asked and I assure you that I've read a lot of posts, articles, etc., and watched a lot of videos but nothing seems to click.
so there we go :
Here are 2 arrays with partial information about every person
let arr1 = [{id:00, name:Ben, city:Philadelphia}, {id:01, name:Alice, city:Frankfurt}, {id:02, name:Detlef, city:Vienna}]
let arr2 = [{id:02, age:18}, {id:00, age:39}, {id:01, age:75}]
And there is the desired final result: an array including the name, city, and age of each person
let arr3 = [{name:Ben, city:Philadelphia, age:39}, {name:Alice, city:Frankfurt, age:75 }, {name:Detlef, city:Vienna, age:18}]
What's the situation? Two arrays both containing objects. each nested object has an id. That id is the common key in each array of objects.
What do you want to do? : I want to create a third array including information from both arrays (from arr1: name and city; from arr2:age).
What have you tried so far? : I couldn't manage to achieve anything worth showing. this minimal example is intended to show you a simple example of my current situation which is: I've got an array that is in the LocalStorage on one hand and an API on the other, both contain some info regarding particular objects (let's say, persons). I want to create an array that will contain all the information regarding each person for easier manipulation afterward (DOM generation, etc.).
I've managed to store both arrays in two "local" arrays but the problem is still there: I can't figure out how to make an array where items are getting their key/value from two separate sources.
Thank you for your help!
You can use reduce method on the arr with array as an inital value, and inside try to find the corrospending item with same id and destruct the object from the id and merge the two object with spread operator.
let arr1 = [{id:00, name:'Ben', city: 'Philadelphia' }, {id:01, name:'Alice', city:'Frankfurt'}, {id:02, name:'Detlef', city:'Vienna'}]
let arr2 = [{id:02, age:18}, {id:00, age:39}, {id:01, age:75}]
const result = arr1.reduce((acc, { id: id1, ...rest1 }) => {
const { id: id2, ...rest2 } = arr2.find(i => i.id === id1)
acc.push({ ...rest1, ...rest2 })
return acc;
}, [])
console.log(result)
You can solve it in various ways, here first I have implemented a dict with key as id to get the value in O(1) while iterating arr2.
So the overall time complexity is O(n+k) where n is len of arr1 and k is len of arr2.
let arr1 = [{id:00, name: "Ben", city: "Philadelphia"}, {id:01, name:"Alice", city:"Frankfurt"}, {id:02, name:"Detlef", city:"Vienna"}];
let arr2 = [{id:02, age:18}, {id:00, age:39}, {id:01, age:75}];
const refMapById = arr1.reduce((refMap, {id, name, city}) => {
refMap[id] = {name, city};
return refMap;
}, {});
const result = arr2.reduce((resultArray, {id, age}) => [...resultArray, { ...refMapById[id],age}], []);
console.log(result);
Cheers!
It will be worth creating a dictionary from one of the arrays anyway since using .find() inside of .reduce() adds an unnecessary nested loop. But instead of reducing the second array as was suggested you can simply .map() it into the result array, like so:
let arr1 = [{ id: 00, name: "Ben", city: "Philadelphia" }, { id: 01, name: "Alice", city: "Frankfurt" }, { id: 02, name: "Detlef", city: "Vienna" }];
let arr2 = [{ id: 02, age: 18 }, { id: 00, age: 39 }, { id: 01, age: 75 }];
const groupedById = arr1.reduce((group, person) => {
group[person.id] = person;
return group;
}, {});
const result = arr2.map((personPartFromSecondArray) => {
const personPartFromFirstArray = groupedById[personPartFromSecondArray.id];
if (typeof personPartFromFirstArray !== "undefined") {
return { ...personPartFromFirstArray, ...personPartFromSecondArray }
}
return personPartFromSecondArray;
});
console.log(result);

Re-ordering one array of objects based on data from another array in Javascript (React)

I've been tasked with taking in an array of objects representing 'consultants' with ids for each and re-arranging them based on the most recent selected consultants of a session booking.
So I have an array of objects of upcoming sessions with the most recent to last:
And I have an array of objects of all consultants:
So therapistId of 'upcomingSessions' match id of 'consultants'
I wrote a method that pulls the therapists from the 'upcomingSessions' into a new array and then concats the remaining, keeping the order of the 'upcomingSessions' therapists.
So the user will see the most recent selected therapists from a dropdown menu.
The method I wrote works but it has a nested forEach() loop because filter() only selected the used consultants but doesn't keep the order.
Here's the method:
const handleUpdatedConsultantList = () => {
// first capture the ids of chosen therapists
const consultantIds = upcomingSessions.map((us) => us.therapistId)
// create an array of remaining therapists
const remainingConsultants = consultants.filter(
(c) => !consultantIds.includes(c.id),
)
// empty array to push in the entire object of each chosen therapist
const recentConsultants: ConsultantType[] = []
// method to push in therapists by most recent
consultantIds.forEach((c) => {
consultants.forEach((co) => {
if (c === co.id) {
recentConsultants.push(co)
}
})
})
// concat most recent with remaining
return recentConsultants.concat(remainingConsultants)
}
My question is, is this the best way to implement this? Nested loops always make me un-easy but maybe it's the only way for keeping the order of the selected consultants?
This gets the filtered chosen consultants but sorts the ids from least to greatest instead of the order that was selected:
const selectedConsultants = consultants.filter((c) => [313, 312, 311, 302].includes(c.id))
A Map will do the job just fine:
const upcomingSessions = [
{therapistId: 5},
{therapistId: 8},
{therapistId: 9},
{therapistId: 7}
];
const consultants = [
{id: 1},
{id: 2},
{id: 3},
{id: 5},
{id: 6},
{id: 7},
{id: 8},
{id: 9},
{id: 10},
{id: 11},
{id: 12}
];
const recentConsultants = new Map(upcomingSessions.map(us => [us.therapistId, ]));
consultants.forEach(c => recentConsultants.set(c.id, c));
console.log([...recentConsultants.values()]);
I think you can more directly get your list of recentConsultants by using a find() lookup when mapping over the consultantIds.
const handleUpdatedConsultantList = () => {
// first capture the ids of chosen therapists
const recentConsultantIds = upcomingSessions.map((us) => us.therapistId);
// map through ids and connect with consultant profiles
const recentConsultants = recentConsultantIds.map((id) =>
consultants.find((c) => c.id === id)
);
// create an array of remaining therapists
const remainingConsultants = consultants.filter(
(c) => !recentConsultantIds.includes(c.id)
);
// concat most recent with remaining
return recentConsultants.concat(remainingConsultants);
};

Object Array to Hash Table

Would like to convert (using Ramda)
var a = [{id: 1, name: 'one'}, {id: 2, name: 'two'},{id: 3, name: 'three'}]
into
var b = {1:'one', 2:'two', 3:'three'}
I'm very new to functional programming and I am open to any ideas.
What I think we must do is, use a reduce function starting with {} and then we want to add on each iteration the id and name of the current element to the hashtable. This is what I came up with which appears very wrong. Am I close?
var a = [{id: 1, name: 'one'}, {id: 2, name: 'two'},{id: 3, name: 'three'},{id: 4, name: 'four'} ]
var b = R.reduce(R.assoc(R.prop('id'), R.prop('name')), {}, a)
b
There are many approaches to this that will work. All the other correct answers helps show that. I'll list a few of my own below. But first,
Why your approach isn't working
You try to do this:
R.reduce(R.assoc(R.prop('id'), R.prop('name')), {}, a)
It's clear what you're trying to do here. The trouble is that R.prop('id') and R.prop('name') are functions. R.assoc does not accept functions; it wants a String (Number will actually serve) and an arbitrary value. So assoc will not work with these in this manner.
One attempt to clean this up is to recognize that functions can be thought of as containers of values. In some -- perhaps surprising, but quite useful -- way, a function is a container of its return value. R.lift is meant to turn functions that work on values into ones that work on containers of values. It works like this: R.multiply accepts numbers. If we lift multiply, the resulting function accepts containers of numbers. Like this:
R.lift(R.multiply)(Maybe.Just(5), Maybe.Just(3)) // Maybe.Just(15)
If we supply lift(multiply) with two functions, then we get back a function that returns the result of multiplying their return values:
const area = R.lift(R.multiply)(prop('width'), prop('height'))
area({width: 5, height: 3})
So perhaps we could update your technique with a lift:
R.reduce(R.lift(R.assoc)(R.prop('id'), R.prop('name'), identity), {}, a)
Unfortunately, this fails again. The trouble this time is that reduce takes a binary callback, supplying both the accumulator and the current value. But R.prop('id') and R.prop('name') don't recognize that. They look for the relevant properties on the accumulator, which simply are not there.
We might still be able to fix this, but at this point, we'd be losing a great deal of the simplicity of this solution. So let's look at other possibilities.
Some solutions
Simple Reduce
Each of these two versions uses a simple reduce call, as your solution tried to do:
const convertToHash = reduce((acc, {id, name}) => merge(acc, {[id]: name}), {})
and
const convertToHash = reduce((acc, {id, name}) => ({...acc, [id]: name}), {})
They are much the same. Both create disposable objects on each iteration. In the first one, you could replace R.merge with Object.assign without any real issues, as the resulting mutation is internal to the function. The second one seems slightly more elegant, but it rebuilds the entire accumulator on each iteration. As the engine optimization for ES6 proceeds, this will likely eventually not be a performance problem, but it might be right now, especially if this is in performance-critical code.
Using zipWith
Ramda's zip functions take two lists and combine them, position by position, into a single list. R.zipWith accepts a function used to combine the two elements into one. Here we use R.objOf, which turns a name and a value into a single-property object. (objOf('foo', 42) //=> {foo: 42}.):
const convertToHash = compmose(mergeAll, lift(zipWith(objOf))(pluck('id'), pluck('name')))
As above, we use lift to make this zipWith(objOf) work with functions. That results in something like [{"1": "one"}, {"2": "two"}, {"3": "three"}], which we then pass to R.mergeAll to create a single object.
Using props and fromPairs
This solutions uses R.props and R.fromPairs. fromPairs accepts a list of name-value pairs (as two-element arrays) and turns them into a single object. props pulls the named properties into a stand-alone array. Mapping this over the original list give us the input to fromPairs:
const convertToHash = compose(fromPairs, map(props(['id', 'name'])))
Although I'm fond of the zipWith solution, and would use it if its output was what I wanted, having to add the mergeAll, makes it harder to comprehend at a glance. And so this solution wins in my mind as the best Ramda choice.
You could achieve this by the following:
var a = [{id: 1, name: 'one'}, {id: 2, name: 'two'},{id: 3, name: 'three'}]
var b = R.reduce((dict, item) => ({ ...dict, [ item.id ] : item.name }), {}, a)
This approach uses es6 syntax to add keys (named via item.id) with value (item.name) to your resulting dictionary, during each iteration of the reduction.
You can create a pipeline (R.pipe) to convert your array of objects to a hash:
Get the values of each (R.map) objects' properties (R.props).
Convert the array of pairs to an object (R.fromPairs).
const a = [{id: 1, name: 'one'}, {id: 2, name: 'two'},{id: 3, name: 'three'}];
const convertToHash = (props) =>
R.pipe(
R.map(R.props(props)),
R.fromPairs
);
const result = convertToHash(['id', 'name'])(a);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>
Try using this code:
var a = [{id: 1, name: 'one'}, {id: 2, name: 'two'},{id: 3, name: 'three'}]
R.map(i=> {
var n = []
n[R.keys(i)[0]] = i.name
return n
},a)
when using es6 you can get a more flexible solution
let a = [{id: 1, name: 'one'}, {id: 2, name: 'two'},{id: 3, name: 'three'}];
let out = a.reduce((acc, {id, name}) => {
acc[id] = name;
return acc;
}, {});
console.log(out)
You can loop your initial array and assign to new object:
var a = [{id: 1, name: 'one'}, {id: 2, name: 'two'},{id: 3, name: 'three'}];
var new_a = {};
a.forEach(function(e) {
new_a[e.id] = e.name;
});
console.log(new_a);

What is the best way to reduce a list with one value to the value? [a] -> a

In one project I generally work with a list that looks like this:
const listOfObjects = [{id: 1, selected: false}, {id: 2, selected: true}, ...]
I filter the array for the selected object like this:
const getSelectedObject = list => list.filter(ob => ob.selected);
So my code looks like this:
const singleObjectList = getSelectedObject(listOfObjects);
singleObjectList supposed to be a list with a single element or an empty list of filter did not match any values.
I then try to get the 1st value of the list like this:
const fold = list => list.reduce((res, r) => r, null);
const ob = fold(singleObjectList)
The result of fold function can be either null or the last value in the list, in case the list has one value [a] it'll return a.
I feel a bit uncomfortable with this use of reduce. I wonder if there is a better/cleaner way to get a single value from a list.
Update
The solution is to use Array.prototype.find
I got great help, some folks have suggested to use:
list[0]
While technically it provides a solution, JavaScript provides useful functions to deal lists like filter, reduce and map. I wondered if I was missing something using filter and reduce combination for this task. And Indeed I was missing the find function.
What is wrong with
list[0]
? Assuming it is certain to have 1 element.
For a result with either the first item or null, you could use a logical OR || to get a null value instead of an undefined for a not existence item.
var a = array[0] || null;
Or use Array#find.
const listOfObjects = [{id: 1, selected: false}, {id: 2, selected: true}],
getSelected = a => a.selected,
find = a => a.find(getSelected),
result = find(listOfObjects);
console.log(result);
Try this
list.reverse().find(item => item.selected)
Select last item from list that has { selected: true }
Remove reverse() for the first item.
const list = [{id: 1, selected: false}, {id: 2, selected: true}]
const result = list.reverse().find(item => item.selected)
console.log(result)
You could try list.find(item => item.selected)
You can do it in one step:
const listOfObjects = [{id: 1, selected: false}, {id: 2, selected: true}, ...]
const getSelected= list=>list.find(o=>o.selected);
let myObject=getSelected(listOfObjects);

Categories

Resources