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)));
Related
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)
I have an array, each subarray of which contains different positions in different order:
[
["apple(2)", "banana(5)"],
["peach(3)", "banana(1)"],
["apple(1)"]
]
I need to sort it on JavaScript (ES6) and i expect to get an array like this:
[
["apple(2)", "banana(5)", "peach(0)"],
["apple(0)", "banana(1)", "peach(3)"],
["apple(1)", "banana(0)", "peach(0)"]
]
Order of each subarray should be the same. If subarray don't have some position, i need to add it with 0 value. Can i using something like map() or sort() function or need to compare it manually?
Here is functional programming approach, using a Map and reduce:
const data = [['apple(2)', 'banana(5)'],['peach(3)', 'banana(1)'],['apple(1)'],];
// Create a Map with default values for each name, i.e. with "(0)":
let names = new Map(data.flat().map(item => [item.replace(/\d+/, ""), item.replace(/\d+/, "0")]));
let result = data.map(row =>
[...row.reduce((map, item) =>
map.set(item.replace(/\d+/, ""), item), // Overwrite default
new Map(names) // Start with clone of original Map
).values()]
);
console.log(result);
You have to loop over to get the keys used. You then have to loop over a second time to get the fill in the missing keys. There are many ways of doing it, this is one.
var data = [
["apple(2)", "banana(5)"],
["peach(3)", "banana(1)"],
["apple(1)"]
];
// match string and number
var re = /([^(]+)\((\d+)\)/;
// Loop over and find all of the keys
var grouped = data.reduce((info, subset, index) => {
subset.forEach(item => {
// find the key and count
var parts = item.match(re);
// have we seen this key?
if (!info[parts[1]]) {
// if not create an array
info[parts[1]] = Array(data.length).fill(0);
}
// set the key index with the count
info[parts[1]][index] = parts[2];
})
return info;
}, {});
// loop over the groups and fill in the set
Object.entries(grouped).forEach(([key, counts], colIndex) => {
counts
.forEach((cnt, rowIndex) => {
data[rowIndex][colIndex] = `${key}(${cnt})`;
})
});
console.log(data);
First get the unique words. Then traverse array of arrays to check if the word is present or not. If it is not present then make the word according to your condition and if present then put the original word to the tmp array. At last sort it for each iteration. By the way, I used regex replace method to get the word.
const data = [
['apple(2)', 'banana(5)'],
['peach(3)', 'banana(1)'],
['apple(1)'],
];
const words = [...new Set(data.flat().map((x) => x.replace(/[^a-z]/gi, '')))];
const ret = data.map((x) => {
const tmp = [];
const newX = x.map((y) => y.replace(/[^a-z]/gi, ''));
for (let i = 0, l = words.length; i < l; i += 1) {
if (newX.includes(words[i])) tmp.push(x.shift());
else tmp.push(`${words[i]}(0)`);
}
return tmp.sort();
});
console.log(ret);
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"}]))
In an array of objects with diff keys, how do I find objects by key using ES6 or Lodash?
const arr = [{a:2}, {b:3}, {fred:10}]
I want the result to be:
=> [{a:2}, {fred:10}]
I don't want to use an omit style approach.
const filtered = arr.filter(obj => obj.hasOwnProperty("a") || obj.hasOwnProperty("fred"));
// or, if you have dynamic / lots of keys:
const keys = ["a", "fred"];
const filtered = arr.filter(obj => keys.some(key => obj.hasOwnProperty(key));
filter method will be useful. Create a function and pass an array of keys. Inside filter function check if the key is matching with the parameter array. If it passed then return that object
var orgObject = [{
a: 2
}, {
b: 3
}, {
fred: 10
}];
function searchByKey(keyNames) {
return orgObject.filter(function(item) {
for (var keys in item) {
if (keyNames.indexOf(keys) !== -1) {
return item
}
}
})
}
console.log(searchByKey(['a', 'fred']))
Basically you want all the objects from the array who have the fields a or fred. You can use the hasOwnProperty() on the objects while filtering.
_.filter(array, elem => elem.hasOwnProperty('a') || elem.hasOwnProperty('fred'));
I have an array that consists of objects with two properties.
One property "value" is a number between 1 and 6.
The other property "id" is a number between 1 and 200.
How can I return the "id" property of all objects with "value" = 1 and write them to a new array?
You should invoke the Array.prototype.filter function there.
var filteredArray = YourArray.filter(function( obj ) {
return obj.value === 1;
});
.filter() requires you to return the desired condition. It will create a new array, based on the filtered results. If you further want to operate on that filtered Array, you could invoke more methods, like in your instance .map()
var filteredArray = YourArray.filter(function( obj ) {
return obj.value === 1;
}).map(function( obj ) {
return obj.id;
});
console.log( filteredArrays ); // a list of ids
... and somewhere in the near future, we can eventually use the Arrow functions of ES6, which makes this code even more beauty:
var filteredArray = YourArray.filter( obj => obj.value === 1 ).map( obj => obj.id );
Pure JS.... no filter/map functions, that are not available for IE < 9
var array = [
{id:10, value:2}, {id:11, value:1}, {id:12, value:3}, {id:13, value:1}
],
result = [];
for(key in array) { if (array[key].value == 1) result.push(array[key].id); }
You can use a combination of Array.prototype.filter and Array.prototype.map.
First, filter only values with value equal to 1.
arr.filter(function (obj) {
return obj.value === 1;
});
Then, you map existing collection to a new array, consisting only of id properties stored in filtered array. So the final code:
var newArr = arr.filter(function (obj) {
return obj.value === 1;
}).map(function (obj) {
return obj.id;
});
The good news is, it's easy, just write
[ for (obj of array) if (obj.value === 1) obj.id ]
The bad news is, it will some time before you can depend on all browsers to do this. It's from a new version of the language called "ES6". But you can try it right now in Firefox!!
for multiple items to be searched from an array into some array of objects, use this
let old = []
ao = [{}, {}], array of objects
let newlist = []
old.map(a => ao.find(x => x.Name == a)!==undefined? newlist.push(ao.find(x => x.Name == a)): null)