Search object in array and update if exist or add - javascript

This is what my array of logged in users looks like:
const connectedUsers = [{
user: {
uuid: 'b62-2dw',
points: 1,
},
id: "1234567"
}];
I will concat this table when a new user logs in to my system:
this.connectedUsers = [
...this.connectedUsers,
{
...payload,
id: client.id
},
];
Then my array looks like this: (I give this to better understand)
const connectedUsers = [{
user: {
uuid: 'b62-2dw',
points: 1,
},
id: "1234567"
},
{
user: {
uuid: '663-dda',
points: 5,
},
id: "33332"
}
];
If the user with the uuid like 663-dda updates his point, I perform this method again.
When I leave it as it is, something like this will be done:
const connectedUsers = [{
user: {
uuid: 'b62-2dw',
points: 1,
},
id: "1234567"
},
{
user: {
uuid: '663-dda',
points: 5,
},
id: "33332"
},
{
user: {
uuid: '663-dda',
points: 6,
},
id: "33332"
}
];
I want to write a very nice (use ES6+) algorithm that first checks if such an object exists in this array (check by id or by user.uuid). If so, update. If not, add a new object. So it should be like this:
const connectedUsers = [{
user: {
uuid: 'b62-2dw',
points: 1,
},
id: "1234567"
},
{
user: {
uuid: '663-dda',
points: 6,
},
id: "33332"
}
];

In the code where you're updating the array when a user logs in, you could do like this:
if (!this.connectedUsers.find(user => user.user.uuid === payload.user.uuid) {
this.connectedUsers = [
...this.connectedUsers,
{ ...payload, id: client.id },
];
}

const connectedUsers = [{
user: {
uuid: 'b62-2dw',
points: 1,
},
id: "1234567"
},
{
user: {
uuid: '663-dda',
points: 5,
},
id: "33332"
},
{
user: {
uuid: '663-dda',
points: 6,
},
id: "33332"
}];
//let take some sample object
const newObDuplicateUuid = {
user: {
uuid: '663-dda',
points: 6,
},
id: "3333290"
}
const newObDuplicateId = {
user: {
uuid: '756-dda',
points: 6,
},
id: "33332"
}
const newObFresh = {
user: {
uuid: '756-dda',
points: 6,
},
id: "3333290"
}
let checkRule = connectedUsers.every(item => item.user.uuid != newObDuplicateUuid.user.uuid && item.id != newObDuplicateUuid.id)
//return false for same uuid
checkRule = connectedUsers.every(item => item.user.uuid != newObDuplicateId.user.uuid && item.id != newObDuplicateId.id)
//return false for same id
checkRule = connectedUsers.every(item => item.user.uuid != newObFresh.user.uuid && item.id != newObFresh.id)
//return true
console.log('Passed validation :'+checkRule);
const result = checkRule ? [...connectedUsers,newObFresh] : 'There is duplicate value';
console.log(result);

This is my approach. One function that covers both cases. Just pass the array and the new entry and it will return the updated list
function updateOrAddUser(listOfUsers, newEntry) {
let found = false;
const updatedUserList = listOfUsers.map(entry => {
if (entry.user.uuid === newEntry.user.uuid) {
found = true;
return newEntry;
}
return entry;
});
if (!found) {
updatedUserList.push(newUser);
}
return updatedUserList;
}

Related

How to filter these arrays in JavaScript?

I have the following arrays:
const players = [{
id: 1,
ip: '198.199.162',
nickname: 'BIG BOSS'
}, {
id: 2,
ip: '198.199.162',
nickname: 'CHICKEN LITTLE'
}, {
id: 3,
ip: '198.199.162',
nickname: 'MR T'
}, {
id: 4,
ip: '198.199.162',
nickname: 'DONUT KING'
}];
const connectedRooms = [{
playerId: 4,
roomId: 1,
playedTime: 300
}, {
playerId: 2,
roomId: 1,
playedTime: 30
}, {
playerId: 1,
roomId: 2,
playedTime: 10
}, {
playerId: 3,
roomId: 3,
playedTime: 45
},
{
playerId: 1,
roomId: 3,
playedTime: 15
}
const rooms = [{
id: 1,
name: 'READY SET GO'
}, {
id: 2,
name: 'CHICKEN WINNER'
}, {
id: 3,
name: 'BURGER TIME'
}];
I need to filter those arrays in a way that I could tell what room has the player played in. My desired output would be the following:
{
key: BIG BOSS,
value: [CHICKEN WINNER, BURGER TIME]
}
So far my code is only listing the IDs of the rooms. I have tried ES6 filter but I only get the room Id as well.
let result = [];
for (let i = 0; i < players.length; i++) {
for (let j = 0; j < connectedRooms.length; j++) {
if (connectedRooms[j].playerId === players[i].id) {
result.push(connectedRooms[j].roomId);
}
}
}
How else could I filter these arrays to obtain the desired output?
result = [];
players.forEach(player=>{
value=[];
rooms.forEach(room=>{
if(connectedRooms.filter(connectedRoom=>connectedRoom.playerId==player.id&&connectedRoom.roomId==room.id).length>0) value.push(room.name);
})
if(value.length>0) result.push({key:player.nickname,value:value});
})
This may help you :
// for each players
players.map((player) => {
return {
key: player.nickname,
value: connectedRooms
// filter connectedRooms where the player is
.filter((connectedRoom) => {
return player.id === connectedRoom.playerId;
})
// for each filtered connectedRooms, you search room's datas
.map((connectedRoom) => {
return rooms.filter((room) => {
return room.id === connectedRoom.roomId;
}).pop().name; // i used `pop` assuming your datas can't have players connected to non-existent room
})
};
});
You need to get all room names from the connectedRooms according to the playerId then assign the room names into value property. In the end, getRooms returns an object holding all target data structures by playerId.
const getRoomNames = (playerId) => connectedRooms.reduce((acc, cr) => {
if(cr.playerId === playerId) {
const roomName = rooms.find(room => cr.roomId === room.id)?.name;
acc.push(roomName);
}
return acc;
}, []);
const getRooms = () => players.reduce((acc, player) => {
acc[player.id] = {
key: player.nickname,
value: getRoomNames(player.id)
}
return acc;
}, {});
Please use this code.
let result = {};
const playerId = 1;
const key = players.filter(val => val.id == playerId).map(val => val.nickname);
const value = connectedRooms.filter(val => val.playerId == playerId).map(val => rooms.filter(value => value.id == val.roomId)[0].name);
result = {key, value};
console.log(result);
Here is a solution that loops through each array once, using reduce.
Finds the user id.
Turns rooms into an object with roomObj[id] = name so I can do a look up instead of looping over rooms again and again.
Builds a new array of playerRooms, filtered by playerId, by adding the room name from roomObj.
function getRoomsBasedOn(nickname) {
// 1
let user = (player) => { return player.nickname == nickname };
let id = players.find(user).id; // crashes if nickname not found
// 2
let roomObj = rooms.reduce((obj, room) => (obj[room.id] = room.name, obj) ,{});
// 3
let playerRooms = connectedRooms.reduce((arr, room) => {
return (room.playerId == id) ? arr.concat(roomObj[room.roomId]) : arr;
}, []);
return {
'key': nickname,
'value': playerRooms
};
}
const players = [{
id: 1,
ip: '198.199.162',
nickname: 'BIG BOSS'
}, {
id: 2,
ip: '198.199.162',
nickname: 'CHICKEN LITTLE'
}, {
id: 3,
ip: '198.199.162',
nickname: 'MR T'
}, {
id: 4,
ip: '198.199.162',
nickname: 'DONUT KING'
}];
const connectedRooms = [{
playerId: 4,
roomId: 1,
playedTime: 300
}, {
playerId: 2,
roomId: 1,
playedTime: 30
}, {
playerId: 1,
roomId: 2,
playedTime: 10
}, {
playerId: 3,
roomId: 3,
playedTime: 45
},
{
playerId: 1,
roomId: 3,
playedTime: 15
}];
const rooms = [{
id: 1,
name: 'READY SET GO'
}, {
id: 2,
name: 'CHICKEN WINNER'
}, {
id: 3,
name: 'BURGER TIME'
}];
console.log( getRoomsBasedOn('BIG BOSS') );

Better way to find data across multiple arrays

getComponentById: (state) => (componentId) => {
return state.articles
.filter(article => Object.keys(article).some(key => {
return ['maps', 'charts', 'tables'].includes(key);
}))
.reduce((acc, article) => {
acc = article.components?.find(c => c.id == componentId);
if (acc) return acc;
acc = article.maps?.find(c => c.id == componentId);
if (acc) return acc;
acc = article.charts?.find(c => c.id == componentId);
if (acc) return acc;
acc = article.tables?.find(c => c.id == componentId);
if (acc) return acc;
})
}
Wonder if there's a better way to rewrite this because the list of components might grow so it feels wrong to just keep adding the lines.
If the id is unique can you just look into every key on every article?
If my guess at your data structure is close you should be able to do something like this
let articles = [
{
maps: [{ id: 1, name: 'map1' }, { id: 2, name: 'map2' }],
charts: [{ id: 3, name: 'charts1' }, { id: 4, name: 'charts2' }],
tables: [{ id: 5, name: 'tables1' }, { id: 6, name: 'tables2' }]
},
{
maps: [{ id: 7, name: 'map3' }, { id: 8, name: 'map4' }],
charts: [{ id: 9, name: 'charts3' }, { id: 10, name: 'charts4' }],
tables: [{ id: 11, name: 'tables3' }, { id: 12, name: 'tables4' }]
}
]
let getComponentById = (componentId) => {
let result = null;
articles.forEach(article => {
Object.keys(article).forEach(key => {
let component = article[key].find(x=> x.id == componentId);
if(component) {
result = component;
}
});
});
return result;
}
console.log(getComponentById(3));
console.log(getComponentById(12));
Credit to #IrKenInvader's answer, I copy data from him.
I use for loop because once you find a component, you can early return and no need to check the rest of the data.
let state = {
articles: [
{
maps: [
{ id: 1, name: "map1" },
{ id: 2, name: "map2" },
],
charts: [
{ id: 3, name: "charts1" },
{ id: 4, name: "charts2" },
],
tables: [
{ id: 5, name: "tables1" },
{ id: 6, name: "tables2" },
],
},
{
maps: [
{ id: 7, name: "map3" },
{ id: 8, name: "map4" },
],
charts: [
{ id: 9, name: "charts3" },
{ id: 10, name: "charts4" },
],
tables: [
{ id: 11, name: "tables3" },
{ id: 12, name: "tables4" },
],
},
],
};
const getComponentById = state => componentId => {
for (let i = 0; i < state.articles.length; i++) {
const filteredKey = Object.keys(state.articles[i]).filter(key =>
["maps", "charts", "tables"].includes(key)
);
for (let j = 0; j < filteredKey.length; j++) {
const foundComponent = state.articles[i][filteredKey[j]].find(
a => a.id == componentId
);
if (foundComponent) return foundComponent;
}
}
return null;
};
const output = getComponentById(state)(12);
console.log(output);

JavaScript string search for array of objects

I need to implement a search function for a table.
I got an array of objects with unnecessary object properties.
I need to map the array to get necessary properties and then do the filtration.
This is my code.
const items = [
{
name: 'pathum',
id: 1,
status: true,
createdAt: 'KKKK',
country: {
name: 'SL',
code: 12,
},
},
{
name: 'kasun',
id: 1,
status: true,
createdAt: 'KKKK',
country: {
name: 'USA',
code: 23,
},
},
{
name: 'hansi',
id: 1,
status: true,
createdAt: 'KKKK',
country: {
name: 'GERMANY',
code: 34,
},
},
];
const tableColumns = ['name', 'country.name'];
const onSearch = (e) => {
e = e.toLowerCase();
const mappedItems = items.map((item) => {
Object.keys(item).forEach((key) => {
if (!tableColumns.includes(key)) delete item[key];
});
return item;
});
if (e) {
const result = mappedItems.filter((item) => {
const str = JSON.stringify(item).toLowerCase();
if (str.search(e) >= 0) return item;
});
return result;
} else {
return mappedItems;
}
};
console.log(onSearch('GERMANY'));
In an item object, I only need to get these two fields
const tableColumns = ['name', 'country.name'];
But this only gives me the name property
const mappedItems = items.map((item) => {
Object.keys(item).forEach((key) => {
if (!tableColumns.includes(key)) delete item[key];
});
return item;
});
My first question is how to map to expect a result like this
{
name: 'pathum',
country: {
name: 'SL',
},
},
Second question is JSON.stringtfy map whole object. So If I search "name" it will return all the objects becasue "name" is there in the all records in the stringtify string.
How do I avoid keys in the object when doing the stringify?
Hope my question is clear to you all.
How do I modify this code to get that expected functionality?
const tableColumns = ['name', 'country'];
const deleteProp = ['code'];
const mappedItems = items.map((item) => {
Object.keys(item).forEach((key) => {
console.log(key);
if (!tableColumns.includes(key)) delete item[key];
if(key == 'country') delete item[key][deleteProp[0]];
});
return item;
});
This may answer your first question.
You can check if the object has any of the tableColumns paths which includes the searched text. And then get a subset of the filtered objects and only include the tableColumns properties
const items=[{name:"pathum",id:1,status:true,createdAt:"KKKK",country:{name:"SL",code:12,},},{name:"kasun",id:1,status:true,createdAt:"KKKK",country:{name:"USA",code:23,},},{name:"hansi",id:1,status:true,createdAt:"KKKK",country:{name:"GERMANY",code:34}}],
tableColumns = ['name', 'country.name'];
function onSearch(array, e) {
const output = [];
for (const o of array) {
const hasProp = tableColumns.some(path => getProperty(o, path).includes(e))
if (hasProp)
output.push(subSet(o, tableColumns))
}
return output
}
function getProperty(o, path) {
return path.split('.').reduce((acc, p) => acc?.[p], o) || ''
}
function subSet(o, paths) {
const output = {}
for (const path of paths) {
let keys = path.split('.'),
last = keys.pop(),
value = o;
const final = keys.reduce((acc, k) => {
value = value?.[k]
return acc[k] ||= {}
}, output);
final[last] = value?.[last];
}
return output;
}
console.log(onSearch(items, 'pat'));
console.log(onSearch(items, 'kasun'));
First, don't change data. You can clone the data and change it.
And, search should be search. Don't put the data formation in it.
Let's start.
const items = [
{
name: 'pathum',
id: 1,
status: true,
createdAt: 'KKKK',
country: {
name: 'SL',
code: 12,
},
},
{
name: 'kasun',
id: 1,
status: true,
createdAt: 'KKKK',
country: {
name: 'USA',
code: 23,
},
},
{
name: 'hansi',
id: 1,
status: true,
createdAt: 'KKKK',
country: {
name: 'GERMANY',
code: 34,
},
},
];
// We will use object to get the fields you want. To reuse, you can add more fields you want.
const tableColumns = {
// id: 1,
name: 1,
country: {
name: 1
}
}
// getting the mapped items
const mappedItems = items.map((item) => {
const temp = {};
Object.keys(item).forEach((key) => {
const target = tableColumns[key];
if (target) {
if (typeof target === 'number'){
temp[key] = item[key];
} else {
temp[key] = {};
Object.keys(target).forEach(subKey => temp[key][subKey] = item[key][subKey]);
}
}
});
return temp;
});
// search function, use local varibles
const onSearch = (array, countryName) => {
return array.find(element => element.country.name.toLowerCase() === countryName.toLowerCase())
}
const searchResult = onSearch(mappedItems, 'germany');
console.log(searchResult);
You can just create a new array using Array.map
const items = [{
name: 'pathum',
id: 1,
status: true,
createdAt: 'KKKK',
country: {
name: 'SL',
code: 12,
},
},
{
name: 'kasun',
id: 1,
status: true,
createdAt: 'KKKK',
country: {
name: 'USA',
code: 23,
},
},
{
name: 'hansi',
id: 1,
status: true,
createdAt: 'KKKK',
country: {
name: 'GERMANY',
code: 34,
},
},
];
let minItems = items.map(function(item) {
return {
"name": item.name,
"country": {
"name": item.country.name
}
}
});
console.log(minItems);

How to merge and return new array from object in es6

Suppose there are two objects.
const a = [
{ id: '1-1-1', name: 'a111' },
{ id: '1-1-2', name: 'a112' },
{ id: '1-2-1', name: 'a121' },
{ id: '1-2-2', name: 'a122' },
{ id: '2-1-1', name: 'a211' },
{ id: '2-1-2', name: 'a212' }
]
const b = ['1-1', '1-2', '2-1']
and the result
{
'1-1':[
{ id: '1-1-1', name: 'a111' },
{ id: '1-1-2', name: 'a112' },
],
'1-2':[
{ id: '1-2-1', name: 'a121' },
{ id: '1-2-2', name: 'a122' },
],
'2-1':[
{ id: '2-1-1', name: 'a211' },
{ id: '2-1-2', name: 'a212' },
]
}
Basically, I want to group the data.
I use includes to check if the item from b to match the id from a. Then construct the new array.
This is my attempt(fiddle):
return b.map(item => a.map(jtem => {
if(jtem.id.includes(item)){
return {
[item]: jtem
}
}
}))
For somehow, it doesn't work.
and, is there a clever way to avoid the nested for loop or map function?
You can do that in following steps:
Apply reduce() on the array b
During each iteration use filter() on the the array a
Get all the items from a which starts with item of b using String.prototype.startsWith()
At last set it as property of the ac and return ac
const a = [
{ id: '1-1-1', name: 'a111' },
{ id: '1-1-2', name: 'a112' },
{ id: '1-2-1', name: 'a121' },
{ id: '1-2-2', name: 'a122' },
{ id: '2-1-1', name: 'a211' },
{ id: '2-1-2', name: 'a212' }
]
const b = ['1-1', '1-2', '2-1']
let res = b.reduce((ac,b) => {
ac[b] = a.filter(x => x.id.startsWith(b));
return ac;
},{})
console.log(res)
As suggested by #Falco is the comments that It would be better to scan over the a once as its large. So here is that version.Actually its better regarding performance
const a = [
{ id: '1-1-1', name: 'a111' },
{ id: '1-1-2', name: 'a112' },
{ id: '1-2-1', name: 'a121' },
{ id: '1-2-2', name: 'a122' },
{ id: '2-1-1', name: 'a211' },
{ id: '2-1-2', name: 'a212' }
]
const b = ['1-1', '1-2', '2-1']
let res = a.reduce((ac,x) => {
let temp = b.find(y => x.id.startsWith(y))
if(!ac[temp]) ac[temp] = [];
ac[temp].push(x);
return ac;
},{})
console.log(res)
Note: startsWith is not supported by I.E. So you can create polyfill using indexOf
if(!String.prototype.startWith){
String.prototype.startsWith = function(str){
return this.indexOf(str) === 0
}
}

Access elements of Array of Objects

I have an array of objects:
const guests = [
{ id: 1, rsvp: true },
{ id: 2, rsvp: false },
{ id: 3, rsvp: true },
{ id: 4, rsvp: false }
];
I would like to write a function which selects only the objects corresponding to IDs (guests) who have rsvp'd.
function selectGuests(guests, id) {
list.forEach(function(id) {
if(id.true) {
push.SelectGuests();
}
});
return selectGuests;
}
however, I am getting gibberish results.
Any help on this one or points in the right direction would be appreciated!
Cheers!
Use array.filter()
DEMO
const guests = [
{ id: 1, rsvp: true },
{ id: 2, rsvp: false },
{ id: 3, rsvp: true },
{ id: 4, rsvp: false }
];
var result = guests.filter(t=>t.rsvp);
console.log(result);
using forEach
function selectGuests(guests)
{
let result = [];
guests.forEach(function (guest) {
if (guest.rsvp) {
result.push(guest);
}
});
return result;
}
const guests = [
{ id: 1, rsvp: true },
{ id: 2, rsvp: false },
{ id: 3, rsvp: true },
{ id: 4, rsvp: false }
];
let a = selectGuests(guests);
console.log(a);
or simpler using filter method
const guests = [
{ id: 1, rsvp: true },
{ id: 2, rsvp: false },
{ id: 3, rsvp: true },
{ id: 4, rsvp: false }
];
let a = guests.filter(function(item){return item.rsvp});
console.log(a);
here is a simplified version without using es6 features.
function selectGuests(guests) {
var selectedGuests = []
// forEach gets the array elements (one by one) as first param
guests.forEach(function(guest) {
if(guest.rsvp) {
selectedGuests.push(guest)
}
})
return selectedGuests
}
// call it as follows
selectGuests(guests)

Categories