Deduplicate array of objects by given property name - javascript

I have an array of objects with about 1500 elements, I am trying to make a new array removing the elements that have a duplicate unique property. But for some reason when I run the function it stops at the first 100 elements of the array. How can I get it to loop through the whole array.
const result = Array.from(new Set(DATA.map((a) => a.Numbers))).map(
(Numbers) => {
return DATA.find((a) => a.Numbers === Numbers);
}
);

Create an object that uses the Numbers property as the keys. Since object keys must be unique, this will remove duplicates. Then get the object values to convert back to an array.
const DATA = [{ Numbers: 1 },{ Numbers: 2 },{ Numbers: 3 },{ Numbers: 4 },{ Numbers: 1 },{ Numbers: 4 }];
const result = Object.values(Object.fromEntries(DATA.map(a => [a.Numbers, a])));
console.log(result)

You're really complicating matters. You're mapping twice, converting the result into a set, and then creating a new array from that set.
It would be much simpler (and more readable) to use a simple loop, and keep a record of the numbers in the objects. If a number already exists splice the object from the array.
This method won't create a new array - you're modifying the existing one - but it will work.
const arr = [{ number: 1 },{ number: 2 },{ number: 3 },{ number: 4 },{ number: 1 },{ number: 4 }];
const numbers = new Set();
for (let i = arr.length - 1; i >= 0 ; i--) {
const { number } = arr[i];
if (numbers.has(number)) arr.splice(i, 1);
numbers.add(number);
}
console.log(arr);

Since no Map-based answers yet (and I believe, Map suits the purpose the best from performance standpoint), I'll post mine:
const src = [{key: 'a', value: 1}, {key: 'c', value: 3}, {key: 'b', value: 2}, {key: 'a', value: 1}, {key: 'c', value: 3}]
const dedupe = (arr, keyProp) => [
...arr
.reduce((acc, obj) =>
(acc.set(obj[keyProp], obj), acc), new Map)
.values()
]
const result = dedupe(src, 'key')
console.log(result)
.as-console-wrapper{min-height:100%;}

The idiom for making an array of distinct objects (also described in this answer) goes like this:
const distinct = DATA.filter((obj, idx) =>
idx === data.findIndex(a => a.Numbers === obj.Numbers));
This filters the input array by selecting all items that, when linearly searched for, return the same index they already have. Thus selecting the first of each such object with the given criteria.
Note: that some of your Numbers were strings and some were actual numbers. (Those with a leading 0 were stored as strings, like '02'.) You could use the less strict == instead of === if you need to deal with situations where the same value may be stored in both string and number format. e.g.: a.Numbers == obj.Numbers.

Related

How to push elements to JS array according to a predetermined schema

I have an array of ids that determines the desired order of elements, which looks like this:
var desired_order = [0,4,2,1,3,...]
In my case it is much longer, so performance is to be considered.
I also have an array of objects with those ids, like this one:
var objects = [{name:"cat",id:0},{name:"dog",id:1},{name:"bird",id:2},{name:"elephant",id:3},
{name:"giraffe",id:4},...]
I need to create a new array var = objects_in_desired_order where these objects will be in the order determined by the desired_order array.
How to do that efficiently?
The only way I can think of is a double for loop where it goes over all possible ids in chronological order and pushes them where they belong. I would use this method if I wouldn't have such big arrays of data.
Thanks in advance!
Turn your objects array into a map id => object:
let m = new Map(objects.map(o => [o.id, o]))
Then,
let objects_in_desired_order = desired_order.map(id => m.get(id))
BTW, stop using var.
First make an object that maps values to ordering keys:
var orderMap = desired_order.reduce(function(map, value, index) {
map[value] = index;
return map;
}, {});
Now you can use that in a sort comparator:
objects.sort(function(o1, o2) {
return orderMap[o1.id] - orderMap[o2.id];
});
The comparator will return a result based on the index of the id key in the original desired order array. The example above will sort the original objects array, so if you want to do the ordering in a new array you could objects.slice() it and make a new one to sort.
This is the same technique as gog's answer, but wrapped in a reusable function.
const reorder = (xs, is, idx = new Map (xs .map (x => [x .id, x]))) =>
is .map (i => idx .get (i))
const desired_order = [0, 4, 2, 1, 3]
const objects = [{name: "cat", id: 0}, {name: "dog", id: 1}, {name: "bird", id: 2},{name: "elephant", id: 3}, {name: "giraffe", id: 4}]
console .log (reorder (objects, desired_order))
.as-console-wrapper {max-height: 100% !important; top: 0}

How to select all objects within an array by their key and add their values together?

So here's what I'm trying to do, I have an application I've been working on that allows users to read articles and take quizzes on them, each article is contained in a category. Category has an _id which is unique. Quiz attempts are held within another table which refrences the article, which refrences the category.
I have looped through all of the quiz attempts and store their keys and values in an array of objects which looks like the following:
const userScoreArray = [];
for(let i = 0; i < data[1].length; i++) {
userScoreArray.push({[data[1][i]['dataValues']['article_id']]: data[1][i]['dataValues']['score'] }) // overall
}
Now I have an array which stores quiz attempts for each category:
[
{4: 5},
{4: 1},
{3: 6},
{5: 0}
// { category: score }
]
How would I be able to get into this array, select all objects with the key of "4" and and then add all of their values together, and then again grab all objects with the key of "5" and add their values together? I was thinking of using a loop to do it but my brain starts to steam right then.
You can use an Array.reduce iterator, then Object.keys() and Object.values() to extract the numbers for comparing and adding
let data = [
{4: 5},
{4: 1},
{3: 6},
{5: 0}
// { category: score }
];
const getScoreFor = n => {
return data.reduce((b, a) => {
return b + (Object.keys(a)[0] == n ? Object.values(a)[0] : 0);
}, 0);
}
console.log(getScoreFor(4))
You can use the reduce method as follows:
const array = [{ 4: 5 }, { 4: 1 }, { 3: 6 }, { 5: 0 }];
const accArray = array.reduce((acc, obj) => {
if (obj[4] ?? obj[5]) {
acc += Object.values(obj)[0];
}
return acc;
}, 0);
console.log(accArray); // 6
MDN ref.:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
By using reduce(), you loop through the array having access at each iteration to an accumulator, the current element and the current index. The initial value of the accumulator, on the above example, is set to zero; at each iteration, we check if the object has a key equal to 4 or 5; if the condition is true we add to the accumulator the value of that object, then it is returned the accumulator (because that is how the reduce method works).
OBS: I used the nullish coalescing operator (??) just in case you need to use 0 as key ({0: any_value}), as the or operator (||) works with falsy values.

Find common elements within dynamic array's object elements Javascript

There were a lot of questions asked about this topic, but I couldn't find the answer that addressed directly the issue I am having.
Here is one: Find common elements in 1 array using Javascript
The first difference is that I have a different type of array, its elements are objects with key-value pair, where key is the string and the value is an array of integers.
The second difference is that array is dynamic meaning that sometimes it may have zero elements and the other times it may have 3 object elements.
I am sharing the sample array below:
const array = [
{"key1":[1,2,3]},
{"key2":[2,3,4]},
{"key3":[2,5,6]},
];
The third difference is that the order of elements matters so that final result should output the values of the first element that exist in all subsequent arrays.
The result should be:
const result = [2];
Since 2 is the only common integer of these three elements.
As mentioned array sometimes might have just 1 or 2 or no elements in it and those cases should be accounted.
Edit 1: as asked in the comments the values of array are unique
Since a value can appear in array only once, you can concat the arrays, count the number of appearances, and filter our those that are not equal to the length of the original array.
const findRecuring = (array) =>
[...
[].concat(...array.map((o) => Object.values(o)[0])) // combine to one array
.reduce((m, v) => m.set(v, (m.get(v) || 0) + 1), new Map()) // count the appearance of all values in a map
] // convert the map to array of key/value pairs
.filter(([, v]) => v === array.length) // filter those that don't appear enough times
.map(([k]) => k); // extract just the keys
/** Test cases **/
console.log('Several:', findRecuring([
{"key1":[6,1,2,3,8]},
{"key2":[2,6,3,4,8]},
{"key3":[2,5,6,8]},
]).join());
console.log('None: ', findRecuring([
{"key1":[9,0,11]},
{"key2":[2,6,3,4,8]},
{"key3":[2,5,6,8]},
]).join());
const array = [
{"key1":[1,2,3]},
{"key2":[2,3,4]},
{"key3":[2,5,6]},
];
You could iterate over and store how often a value appears in the array for each value:
var common=new Map();
array.forEach(function(obj){
//var values=new Set(Object.values(obj)[0]);//make unique values out of array
var values=Object.values(obj)[0];//no need for uniqueness as OP states that they are already unique..
values.forEach(function(val){
common.set(val,(common.get(val)||0)+1);
});
});
Now youve got a map with all elements and their appearance in the main array. So now you can simply compare:
var result=[];
common.forEach(function(appearance,el){
if(appearance===array.length){
result.push(el);
}
});
http://jsbin.com/xuyogahija/edit?console
You could get first the arrays in an array without using different keys and then lookup each element if it is in the other array.
let array = [{ key1: [1, 2, 3] }, { key2: [2, 3, 4] }, { key3: [2, 5, 6] }],
result = array
.map(o => o[Object.keys(o)[0]] || [])
.reduce((a, b) => a.filter(c => b.includes(c)));
console.log(result);

Add JavaScript Array of Object properties values to get a total count number

Based on a JavaScript array of objects like this one below, is it possible to add the number values on the count property of all the items together and store in a variable as a total count without iterating over the array?
var category_names_array = [
{name: 'cat1', count: 13},
{name: 'cat2', count: 23},
{name: 'cat3', count: 33}
];
It is uclear what did you mean by iterating. You must access all indexes of an array to do that. If you just want to avoid for loop, then use Array.prototype.reduce function.
var total = category_names_array.reduce(function(a, b){
return a + b.count;
}, 0);
If we define iterating as ordered accesing of array's indexes, then we must iterate over the array if we want to process all its indexes. Even stringifying object using JSON and then filtering and evaluating string is still iterating - JSON must iterate over the array to stringify it. Whatever you do, you must iterate over array's indexes to process it. Note that even adding two integers is some kind of iterating - you iterate over their bits to add them together.
Yes, you can.
sum(counts(array))
My, how very readable that is. But ooops, now we need to write counts. The easiest way is
function counts(array) { return array.map(elt => elt.count); }
Does map constitute "iteration"? Depends on your definition. Let's say no, since the iteration is happening inside the engine!!
All that remains is to write sum. We'll leave that as an exercise for the reader.
It is not possible to achieve what you ask without iterating over the array.
What you want to achieve is a reduce operation which in my opinion is a linear time algorithm. i.e it is guranteed to iterate through every element in the JS array.
An sample solution using Lodash JS
var category_names_array = [
{name: 'cat1', count: 13},
{name: 'cat2', count: 23},
{name: 'cat3', count: 33}
];
var total = _.reduce(category_names_array, function(totalCount,item) {
return totalCount + item.count;
},0);
Complexity O(n)
You could use a recursive approach
function sum(a, s) {
s = s || 0;
a = a.slice();
return a.length ? sum(a, s + a.pop().count) : s;
}
var category_names_array = [{ name: 'cat1', count: 13 }, { name: 'cat2', count: 23 }, { name: 'cat3', count: 33 }];
console.log(sum(category_names_array));
ES6
function sum(a) {
var f = (a, ...r) => (s += a.count, r.length && f(...r)),
s = 0;
f(...a);
return s;
}
var category_names_array = [{ name: 'cat1', count: 13 }, { name: 'cat2', count: 23 }, { name: 'cat3', count: 33 }];
console.log(sum(category_names_array));

Sort array containing objects based on another array [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
JavaScript - Sort an array based on another array of integers
Javascript - sort array based on another array
If I have an array like this:
['one','four','two']
And another array like this:
[{
key: 'one'
},{
key: 'two'
},{
key: 'four'
}]
How would I sort the second array so it’s key property follows the order of the first? In this case, I want:
[{
key: 'one'
},{
key: 'four'
},{
key: 'two'
}]
We can use the sort() function to do this by passing it a custom function which does the comparison. This function has to return 3 possible values given a or b to compare:
return -1 if a is indexed lower than b
return 0 if a is considered equal to b
return 1 if a is indexed greater than b
With this in mind, we can define a function such as this:
function sortFunction(a,b){
var indexA = arr.indexOf(a['key']);
var indexB = arr.indexOf(b['key']);
if(indexA < indexB) {
return -1;
}else if(indexA > indexB) {
return 1;
}else{
return 0;
}
}
This function will take in the objects you defined in your array, and find where that value is in the arr array, which is the array you're comparing to. It then compares the index, and returns the values as needed.
We use this function by passing the function into the sort() function as such:
testArray.sort(sortFunction)
where testArray is the array you're trying to sort.
You can take a look at here, where I did this example, and you can see the second object in your array being "alerted" to you, before and after the sort function was called. http://jsfiddle.net/Sqys7/
Here's my take on it:
function orderArray(array_with_order, array_to_order) {
var ordered_array = [],
len = array_to_order.length,
len_copy = len,
index, current;
for (; len--;) {
current = array_to_order[len];
index = array_with_order.indexOf(current.key);
ordered_array[index] = current;
}
//change the array
Array.prototype.splice.apply(array_to_order, [0, len_copy].concat(ordered_array));
}
Sample implementation:
var array_with_order = ['one', 'four', 'two'],
array_to_order = [
{key: 'one'},
{key: 'two'},
{key: 'four'}
];
orderArray(array_with_order, array_to_order);
console.log(array_to_order); //logs [{key: 'one'}, {key: 'four'}, {key: 'two'}];
The usual fiddle: http://jsfiddle.net/joplomacedo/haqFH/

Categories

Resources