Get an item inside an array by its property - javascript

I would like to assign the output from Array.filter to an existing array.
I'm working on nodejs socket service, and I have an array structured like this:
GROUPS = [{id: 1, members: ['user1', 'user2', ... ]}];
I would like to remove a user doing a map on GROUPS, with this:
GROUPS.map((group) => {
group.members.map((member) => {
const connectedClients = Array.from(wss.clients);
const userIsConnected = connectedClients.some(existingMember => existingMember.id === member.id);
if (!userIsConnected) {
console.log('member: ', member.userID); // userid: 12345678
console.log('group_id: ', group.id); // groupId: 123456677
console.log('1. ', GROUPS[group.id]); // undefined
GROUPS[group.id].members = group.members.filter(memberInGroup => memberInGroup !== member.id);
console.log('group: ', GROUPS[group.id].members.length);
}
});
});
How can I do that association?
GROUPS[group.id].members = group.members.filter(memberInGroup => memberInGroup !== member.id);
Thank you!

MDN docs on map
arr.map(function callback(currentValue[, index[, array]]) {
// Return element for new_array
}[, thisArg])
Your issue is, you're using the group.id as index, but basic knowledge about iterations is that first elements index is 0, whilst your IDs value is 1. Having an array with 10 elements, last elements index is 9, arr[9], trying to call arr[10] will result in error, like in your example.
You can use forEach cuz you don't seem to need the array output
GROUPS.forEach((group, index, arr) => {
arr[index] = "..";
});

Related

How does one, for nested arrays, rearrange the order in a way that the last string item of an array equals the first string item of the following one?

I need to achieve this result
[['peanuts', 'butter'], ['butter', 'jelly'], ['jelly', 'bananas'], ['bananas', 'apples']]
From this array
[['butter', 'jelly'], ['bananas', 'apples'], ['peanuts', 'butter'], ['jelly', 'bananas']]
I want the second element of each array to match the first element of the next one.
I think sort function is the best option here, but I have tried this and this doesn't work (actually works but not with all arrays somehow)
.sort(([a, b], [c, d]) => {
return b === c ? -1 : 1
})
Sorting is not the best method to solve this problem. Instead, you can create lookup tables for the first and second elements and go through them in sequence.
const arr = [
['butter', 'jelly'],
['bananas', 'apples'],
['peanuts', 'butter'],
['jelly', 'bananas']
];
const lookupFirst = {},
lookupSecond = {};
for (let i = 0; i < arr.length; i++) {
lookupFirst[arr[i][0]] = i;
lookupSecond[arr[i][1]] = i;
}
const next = arr.map(x => lookupFirst[x[1]]);
let curr = arr.findIndex(x => !lookupSecond[x[0]]);
const res = [];
do {
res.push(arr[curr]);
} while ((curr = next[curr]) !== undefined);
console.log(JSON.stringify(res));
Sorting will not help, instead one needs an algorithm which, for instance, starts by searching for the only array that does not have any matching item which links it to any of the other arrays.
This array's first item (a string) then is the link to this very arrays previous array that's last item does match the before mentioned first item.
The above described process splices every matching array from the input value and collects (unshift) them within a result array, thus it mutates the input value, and therefore just needs to be continued until the input value got emptied.
function getCopyInDominoesLikeOrder(list) {
// in order to not directly mutate the input value.
list = Array.from(list);
// get the array where its last item does not match
// any other array's first item, which makes it the
// last array of the return value.
let itemListIndex = list.findIndex(aList =>
list.every(bList => aList[aList.length - 1] !== bList[0])
);
let itemList = list.splice(itemListIndex, 1)[0];
const result = [itemList]; // return value.
let firstItem = itemList[0];
// mutate/reduce the input value's copy while looking
// for the array where its last item matches the first
// item of the previously found/extracted (linked) array.
while (list.length !== 0) {
itemListIndex = list.findIndex(aList =>
aList[aList.length - 1] === firstItem
);
itemList = list.splice(itemListIndex, 1)[0]; // mutate/reduce.
result.unshift(itemList); // aggregate return value.
firstItem = itemList[0];
}
return result;
}
const arr = [
['butter', 'jelly'],
['bananas', 'apples'],
['peanuts', 'butter'],
['jelly', 'bananas'],
];
console.log(
'original :: arr :',
arr
);
console.log(
'sorted :: getCopyInDominoesLikeOrder(arr) :',
getCopyInDominoesLikeOrder(arr)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
It looks like you only need to extract the first element from each array in the sort callback, then return the lexiographic difference:
const arr = [['c','d'], ['a','b'], ['d','e'], ['b','c']];
arr.sort((a, b) => a[0].localeCompare(b[0]));
console.log(arr);
.sort(([a], [b]) => a > b)

Looping over two arrays in javascript simultaneously

let masterList=[{id:1,name:'Abc'},{id:2,name:'Def'},{id:3,name:'Ghi'}];
let selectedList=[2,3];
The desired result is to have
//desiredList=[{id:2,name:'Def'},{id:3,name:'Ghi'}]
Currently what I am doing is
let parsedArray = [];
masterList.forEach(mItem => {
selectedList.forEach(sItem => {
if (mItem.id === sItem) {
parsedArray.push(mItem);
}
});
});
desiredList=parsedArray
I do not find this method efficient when iterating over large arrays, is there any logic, any inbuilt javascript operators using which I can achieve the same?
You could take a map with id as key and the object as value and map the wanted values from the map by mapping selectedList.
This approach uses the order from selectedList.
var masterList = [{ id: 1, name: 'Abc' }, { id: 2, name: 'Def' }, { id: 3, name: 'Ghi' }],
selectedList = [2, 3],
result = selectedList.map(Map.prototype.get, new Map(masterList.map(o => [o.id, o])));
console.log(result);
It should be a simple filter on the masterList:
masterList.filter(item => selectedList.includes(item.id));
You can use Array filter() to do this.
Demo:
let masterList=[{id:1,name:'Abc'},{id:2,name:'Def'},{id:3,name:'Ghi'}];
let selectedList=[2,3];
let desiredList = masterList.filter((val) => selectedList.includes(val.id));
console.log(desiredList)
You can first convert selectedList to Set and then use filter() method array of objects.
You can use Set.prototype.has to check whether the id of the objects exists in the set or not. And this method has O(1) time-complexity. So the time-complexity of the whole algorithm will be linear.
let masterList=[{id:1,name:'Abc'},{id:2,name:'Def'},{id:3,name:'Ghi'}];
let selectedList = [2,3];
let set = new Set(selectedList);
let res = masterList.filter(x => set.has(x.id));
console.log(res)
Turn the first array into an object indexed by id first, so you can look up the appropriate matching object in O(1) time, and then you can .map the selectedList:
const masterList=[{id:1,name:'Abc'},{id:2,name:'Def'},{id:3,name:'Ghi'}];
const selectedList=[2,3];
const masterById = masterList.reduce((a, obj) => {
a[obj.id] = obj;
return a;
}, {});
const desiredList = selectedList.map(id => masterById[id]);
console.log(desiredList);
Try this:
let masterList=[{id:1,name:'Abc'},{id:2,name:'Def'},{id:3,name:'Ghi'}];
let selectedList=[2,3];
const result = masterList.filter(({id})=> selectedList.includes(id));
console.log(result);

Find an index of array item from another json array item

I am looking to find out an index and group the item belong to in a parent json group, how can I do it?
I am open to reformat the json as well if need be,
I tried JSON.stringify() but it returns the wrong index as well.
let Content = {
group1: [
[{content:"hello"},{content:"world"}],
[{content:"hello1"},{content:"world"}],
[{content:"hello2"},{content:"world"}],
[{content:"hello3"},{content:"world"}],
[{content:"hello4"},{content:"world"}],
[{content:"hello5"},{content:"world"}],
],
group2: [
[{content:"hello10"},{content:"world"}],
[{content:"hello11"},{content:"world"}],
[{content:"hello12"},{content:"world"}],
[{content:"hello13"},{content:"world"}],
[{content:"hello14"},{content:"world"}],
[{content:"hello15"},{content:"world"}],
],
};
// let currentItem = {type:'group2',index:5};
// let currentItemContent = Content[currentItem.type][currentItem.index];
let obj = [{content:"hello15"},{content:"world"}];
let newIndex = Content["group1"].indexOf(obj);
let type = "group1";
if(newIndex < 0)
{
type="group2"
console.log(Content["group2"]);
newIndex = Content["group2"].indexOf(obj);
}
console.log({"type":type,"index":newIndex});
expected: {type:'group2',index:5}
Loop through the Content object using for...in. Check if the given array is in each group by using findIndex. Since both the objects in the array seem to be in order, you can simply compare the string returned by JSON.stringify
let Content={group1:[[{content:"hello"},{content:"world"}],[{content:"hello1"},{content:"world"}],[{content:"hello2"},{content:"world"}],[{content:"hello3"},{content:"world"}],[{content:"hello4"},{content:"world"}],[{content:"hello5"},{content:"world"}]],group2:[[{content:"hello10"},{content:"world"}],[{content:"hello11"},{content:"world"}],[{content:"hello12"},{content:"world"}],[{content:"hello13"},{content:"world"}],[{content:"hello14"},{content:"world"}],[{content:"hello15"},{content:"world"}]]}
function find(input, search) {
for (const type in input) {
const group = input[type];
const index = group.findIndex(a => JSON.stringify(a) === JSON.stringify(search));
if (index != -1)
return { type, index }
}
return null
}
console.log(find(Content, [{content:"hello15"},{content:"world"}]))
console.log(find(Content, [{content:"hello"},{content:"world"}]))
You could also use Array.find in combination with Object.keys and Array.some. The array comparison you can do via JSON.stringify however remember that if your keys are in different order that would not work:
[{content:"world"},{content:"hello"}] vs [{content:"hello"},{content:"world"}]
would not match as you would expect since you are matching on strings and they are now different.
let Content = { group1: [ [{content:"hello"},{content:"world"}], [{content:"hello1"},{content:"world"}], [{content:"hello2"},{content:"world"}], [{content:"hello3"},{content:"world"}], [{content:"hello4"},{content:"world"}], [{content:"hello5"},{content:"world"}], ], group2: [ [{content:"hello10"},{content:"world"}], [{content:"hello11"},{content:"world"}], [{content:"hello12"},{content:"world"}], [{content:"hello13"},{content:"world"}], [{content:"hello14"},{content:"world"}], [{content:"hello15"},{content:"world"}], ], };
let findArray = (data, obj) => {
let index, group = Object.keys(data).find((k,i) => {
index = i
return data[k].some(x => JSON.stringify(x) === JSON.stringify(obj))
})
return { index, group }
}
console.log(findArray(Content, [{content:"hello"},{content:"world"}]))
console.log(findArray(Content, [{content:"hello10"},{content:"world"}]))

Get objects in array with duplicated values

I need to get elements from an array of objects where one of that object's properties (name in this case) is duplicated--in other words, appears in some other object in the array.
data
var data = [
{id:1, name:"sam", userid:"ACD"},
{id:1, name:"ram", userid:"SDC"},
{id:1, name:"sam", userid:"CSTR"}
];
i need to check all row and get all the array value where name property is duplicating.
the expected output:
[
{id:1, name:"sam", userid:"ACD"},
{id:1, name:"sam", userid:"CSTR"}
]
my code
Array.from(data).map(x => x.name)
but it is returning all the values.
The code should not create any performance issue because array will contain more than 500 rows.
Angular is a framework, not a language. There is no Angular in your problem.
Let me understand if I understood well. You have an array of objects and you want to keep all the elements that are duplicate and get rid of others, all right? You can try:
data.reduce((acc, value, i, arr) => {
if(acc.some(v => v.name === value.name)) return acc;
let filtered = arr.filter(v => v.name === value.name);
return filtered.length > 1 ? acc.concat(filtered) : acc;
}, []);
Or you can sort your array in first instance, in order to improve performance:
const sort = (a, b) => a.name.toUpperCase() < b.name.toUpperCase() ? -1 : 1;
let duplicates = [];
let sortedArray = data.sort(sort);
for(let i=0; i<sortedArray.length - 1; i++) {
if(sortedArray[i].name === sortedArray[i+1].name) {
duplicates.push(sortedArray[i], sortedArray[i+1]);
i++;
}
}
The brute force approach would be to filter the array to keep only those elements with duplicated names, as expressed by the filter function duplicateName.
// Is there more than one element in an array satisfying some predicate?
const hasMultiple = (arr, pred) => arr.filter(pred).length > 1;
// Is this element a duplicate in the context of the array?
const duplicateName = (elt, idx, arr) => hasMultiple(arr, e => e.name === elt.name);
// Test data.
var data = [
{id:1,name:"sam", userid:"ACD"},
{id:1,name:"ram", userid:"SDC"},
{id:1,name:"sam", userid:"CSTR"}
];
console.log(data.filter(duplicateName));
However, this is going to have poor performance (O(n^2)) in the case of many elements. To solve that problem, you're going to need to preprocess the array. We'll create an object with a property for each name, whose value is an array of all the elements in which that name occurs. This operation is usually called groupBy. Several popular libraries such as underscore will provide this for you. We'll write our own. After grouping, we will filter the object of groups to remove those with only one member.
// Group an array by some predicate.
const groupBy = (arr, pred) => arr.reduce((ret, elt) => {
const val = pred(elt);
(ret[val] = ret[val] || []).push(elt);
return ret;
}, {});
// Filter an object, based on a boolean callback.
const filter = (obj, callback) => Object.keys(obj).reduce((res, key) => {
if (callback(obj[key], key, obj)) res[key] = obj[key];
return res;
}, {});
// Remove groups with only one element.
const removeNonDups = groups => filter(groups, group => group.length > 1);
// Test data.
var data = [
{id:1,name:"sam", userid:"ACD"},
{id:1,name:"ram", userid:"SDC"},
{id:1,name:"sam", userid:"CSTR"}
];
console.log(removeNonDups(groupBy(data, elt => elt.name)));

match an object in an array of objects and remove

After 2 days of fighting this problem I'm hoping someone can help me out.
I have two arrays of objects, like this:
let oldRecords = [
{name: 'john'},
{name: 'ringo'}
];
let newRecords = [
{name: 'paul'},
{name: 'john'},
{name: 'stuart'}
];
I am trying to end up with a function that returns named variables containing a list of data thats been added (exist in newRecords but not in oldRecords) and a list of data that has been removed (exists in oldRecords but not in newRecords).
for example
const analyse = (older, newer) => {
let added, removed;
// ... magic
return {added, removed}
}
const {added, removed} = analyse(oldRecords, newRecords);
I won't post all the code I've tried inside this function as I have tried to map, reduce and loop through both arrays creating temporary arrays for the last 48 hours and I could now fill a book with code I've written and deleted. I have also used underscore.js methods like reject/find/findWhere which all got me close but no cigar.
the main issue I am having is because the arrays contain objects, its super easy if they contain numbers, i.e.
var oldRecords = [1, 3];
var newRecords = [1, 2, 4]
function analyse (old, newer) {
let added = [];
let removed = [];
old.reduce((o) => {
added = added.concat(_.reject(newer, (num) => num === o ));
});
newer.reduce((n) => {
removed = _.reject(old, (num) => num === n );
});
return {added, removed}
}
const {added, removed} = analyse(oldRecords, newRecords);
How can I achieve the above but with objects not arrays?
n.b. I tried modifying the above and using JSON.stringify but it didn't really work.
EDIT: important point I forgot to add, the object structure adn it's keys are dynamic, they come from a database, so any individual checking of a key must also be dynamic, i.e. not a hard coded value
You could use reject and some to check for inclusion in the appropriate sets. The isEqual function is used to check for equality to handle dynamic keys:
const analyse = (older, newer) => {
let added = _.reject(newer, n => _.some(older, o =>_.isEqual(n, o)));
let removed = _.reject(older, o => _.some(newer, n => _.isEqual(n, o)));
return {added, removed}
}
You could try this:
const analyse = (older, newer) => {
let removed = older.filter(newItem => {
return newer.filter(oldItem => {
return _.isEqual(newItem, oldItem);
}).length === 0
});
let added = newer.filter(oldItem => {
return older.filter(newItem => {
return _.isEqual(newItem, oldItem);
}).length === 0
});
return {added, removed};
}
You can first create function to check if two object are equal, end then use filter() and some() to return result.
let oldRecords = [
{name: 'john'},
{name: 'ringo'}
];
let newRecords = [
{name: 'paul'},
{name: 'john'},
{name: 'stuart'}
];
function isEqual(o1, o2) {
return Object.keys(o1).length == Object.keys(o2).length &&
Object.keys(o1).every(function(key) {
return o2.hasOwnProperty(key) && o1[key] == o2[key]
})
}
var result = {}
result.removed = oldRecords.filter(e => !newRecords.some(a => isEqual(e, a)))
result.added = newRecords.filter(e => !oldRecords.some(a => isEqual(e, a)))
console.log(result)

Categories

Resources