Find common elements within dynamic array's object elements Javascript - 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);

Related

Array, return original index from a sorted array in javascript

'I wish to sort an array in numerical order but once it is sorted I wish to be able to find the original index.
For example the original array:
ptsGP = [3,8,2,5,6,9,8,4]
I am using the following code below to sort the array:
arr = ptsGP;
var arr2 = arr.map(function(o, i){return {idx: i, obj: o}; }).sort(function(a, b) {
return b.obj - a.obj;
});
for(var i = 1, j = arr2.length; i <= j; i++){
document.write('i:' + i + ' = arr2[i].obj: PTS: ', arr2[i-1].obj+"<br/>");
}`
This is fine as the sorted array is :
arr = [2,3,4,5,6,8,8,9];
How can I find the index of sorted number in the original array? In this case it would be :
Index on original array would be = [2,0,7,3,4,1,6,5]
I know I could use map on the original array but how can I deal with duplicate numbers i.e, in this case I have two number 8's within the array?
You can achieve it by following below steps :
Creating a deep copy of an original array by using spread operator. So that we can get proper indexing.
Now we can iterate deep copy array to get the index of the elements from an original array.
Regarding duplicate values we can check via .indexOf() and .lastIndexOf() methods.
via and then via comparison. For fetching the correct index of duplicate values I wrote a logic based on the count of duplicate value.
Working Demo :
// Original array.
const originalArray = [3, 8, 2, 5, 6, 9, 8, 4];
// Creating a deep copy of an original array.
const deepCopy = [...originalArray].sort(function(a, b){
return a-b
});
// result array
const arr = [];
// count to get the index based on duplicate values.
let count = 0;
// Iterating deepCopy array to get the actual index.
deepCopy.forEach((elem) => {
// Checking for duplicate value in an array
if (originalArray.indexOf(elem) === originalArray.lastIndexOf(elem)) {
// This line of code execute if there is no duplicates in an array.
arr.push(originalArray.indexOf(elem))
} else {
// This line of code execute if there is duplicate values in an array.
count++;
// Inserting the index one by one.
arr.push(originalArray.indexOf(elem, count))
}
});
// Result array.
console.log(arr);

Deduplicate array of objects by given property name

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.

concatenate problems with concat function

In this case of concat() function, i don't get why the value of the length property is not concatenated to array. Also when i change the value of length property to some random value, the first two properties are ignored as well when i concatenate them.
1st case:
let arr = [1, 2];
let arrayLike = {
0: "something",
1: "else",
[Symbol.isConcatSpreadable]: true,
length: 2
};
alert( arr.concat(arrayLike) ); // 1,2,something,else
2nd case:
let arr = [1, 2];
let arrayLike = {
0: "something",
1: "else",
[Symbol.isConcatSpreadable]: true,
length: "random",
};
console.log( arr.concat(arrayLike) ); // 1,2
Your arrayLike object is sort of mimicking being an array like arr. So if you're treating it like a normal array, then why would you expect the value of length to be counted as a value in the array?
In your actual array, arr, you would have arr.length == 2. But 2 is not a value in the array, it just tells you how many values are in the array (two values, 1 and 2). This is how JavaScript knows how many values to look for. If you were to set arr.length = 1, suddenly JavaScript would only show one value instead of two. See here:
let arr = [1, 2];
console.log(arr); //[1, 2]
arr.length = 1;
console.log(arr) //[1]
Similarly, your length: 2 property in your arrayLike object is being used to represent an array with 2 elements. If you set it to something that isn't a number, JavaScript no longer knows how many values could be in the "array", so it apparently simply counts it as 0 - an empty array.

return array of indices of all values in an array in Javascript

I have an array of arrays, and I want to get an array of the indexes of elements.
I start with this:
[
[1,2,3],
[-1],
[-1],
[4,5,6],
[-1],
[],
[]
]
And I want:
[1,2,4]
Some of the elements might be not be filled yet (they will get filled over time), but I want to specify which ones are deliberately empty. That's what the -1 signifies (I could as easily use null).
I'm sure filter is the right thing to use, but I don't want the element; I just want the index of the element.
I'm sure I can do it with a few loops and iterations, but that seems to be overkill.
A little more manual that it could be, but very understandable.
let input = [
[1,2,3],
[-1],
[-1],
[4,5,6],
[-1],
[],
[]
];
let output = [];
// Loop over original array
input.forEach(function(item, index){
// If length of current item is 1 and that one item is the "deliberately
// empty" indicator, push its index into result array
item.length === 1 && item[0] === -1 ? output.push(index) : null;
});
console.log(output);
You can use Array#reduce's third parameter to access each element's index, then use the callback to conditionally extend the result array if the current element matches your flag variable.
const result = [
[1,2,3],
[-1],
[-1],
[4,5,6],
[-1],
[],
[]
].reduce((a, e, i) => e.length === 1 && e[0] === -1 ? a.concat(i) : a, []);
console.log(result);
Having said that, your original version as well as your suggestion of using null rather than the [-1] magic number seems less prone to error. Here is code which tests for null elements as distinct from empty arrays:
const emptyIdxes = [
[1,2,3],
[],
null,
[7,8,9],
null
].reduce((a, e, i) => e === null ? a.concat(i) : a, []);
console.log(emptyIdxes);
This is how I implemented Scott Marcus' suggestion of using a forEach loop to manually generate a new array.
While other solutions using map(), reduce() or filter() may be faster or require less processing cycles, I prefer this solution for being much more readable and much more debuggable. This is important, since hopefully this code will outlive my involvement in it, and the next coder should be able to decipher it easily.
Below is an example input. It is an (outer) array of the first ten weeks of a darts game, each element is its own (inner) array, containing the scores of the eight teams. Only the first two weeks have been scored so far.
Weeks 7 and 8 are holidays, and so should be greyed out in the HTML table. Holidays can be marked at the beginning of the season, regardless of how many weeks have been played.
var arrScore = [
[2,1,1,2,2,1,2,1],
[2,2,2,0,1,1,3,1],
[],
[],
[],
[],
[null],/* holiday */
[null],/* holiday */
[],
[] /* etc. */
];
Because I process the holidays in a separate function from the scores, I want a nice clean (new) array so I can run through the weeks and just mark up holidays, independent of processing the scoring. And empty weeks that are holidays (and thus, will never have scores) must be distinguishable from weeks that merely don't have scores yet.
The (new) array I want, based on above example, would be [6,7], which is the (zero-based) index of the [null] elements.
arrScore.forEach(function(item, index){
item.length === 1 && item[0] === null ? holidays.push(index) : null;
});
console.log(holidays);
=
[6,7]
The forEach() merely iterates over the (outer) array, and if it finds a element that
is an (inner) array of length one, and
has the first element of that (inner) array as null,
it simply pushes the index of that element (of the outer array) into a new holidays array.
You can use map function too
var arr = [
[1, 2, 3],
[-1],
[-1],
[4, 5, 6],
[-1],
[],
[]];
var arr2 = [];
const map1 = arr.map(x => x == -1 ? arr2.push(arr.indexOf(x)) : "");
console.log(arr2);

How to remove element from an array in JavaScript?

var arr = [1,2,3,5,6];
Remove the first element
I want to remove the first element of the array so that it becomes:
var arr = [2,3,5,6];
Remove the second element
To extend this question, what if I want to remove the second element of the array so that it becomes:
var arr = [1,3,5,6];
shift() is ideal for your situation. shift() removes the first element from an array and returns that element. This method changes the length of the array.
array = [1, 2, 3, 4, 5];
array.shift(); // 1
array // [2, 3, 4, 5]
For a more flexible solution, use the splice() function. It allows you to remove any item in an Array based on Index Value:
var indexToRemove = 0;
var numberToRemove = 1;
arr.splice(indexToRemove, numberToRemove);
arr.slice(begin[,end])
is non destructive, splice and shift will modify your original array
The Array.prototype.shift method removes the first element from an array, and returns it. It modifies the original array.
var a = [1,2,3]
// [1,2,3]
a.shift()
// 1
a
//[2,3]
Maybe something like this:
arr=arr.slice(1);
Wrote a small article about inserting and deleting elements at arbitrary positions in Javascript Arrays.
Here's the small snippet to remove an element from any position. This extends the Array class in Javascript and adds the remove(index) method.
// Remove element at the given index
Array.prototype.remove = function(index) {
this.splice(index, 1);
}
So to remove the first item in your example, call arr.remove():
var arr = [1,2,3,5,6];
arr.remove(0);
To remove the second item,
arr.remove(1);
Here's a tiny article with insert and delete methods for Array class.
Essentially this is no different than the other answers using splice, but the name splice is non-intuitive, and if you have that call all across your application, it just makes the code harder to read.
Others answers are great, I just wanted to add an alternative solution with ES6 Array function : filter.
filter() creates a new array with elements that fall under a given criteria from an existing array.
So you can easily use it to remove items that not pass the criteria. Benefits of this function is that you can use it on complex array not just string and number.
Some examples :
Remove first element :
// Not very useful but it works
function removeFirst(element, index) {
return index > 0;
}
var arr = [1,2,3,5,6].filter(removeFirst); // [2,3,4,5,6]
Remove second element :
function removeSecond(element, index) {
return index != 1;
}
var arr = [1,2,3,5,6].filter(removeSecond); // [1,3,4,5,6]
Remove odd element :
function removeOdd(element, index) {
return !(element % 2);
}
var arr = [1,2,3,5,6].filter(removeOdd); [2,4,6]
Remove items not in stock
const inventory = [
{name: 'Apple', qty: 2},
{name: 'Banana', qty: 0},
{name: 'Orange', qty: 5}
];
const res = inventory.find( product => product.qty > 0);
There are multiple ways to remove an element from an Array. Let me point out most used options below. I'm writing this answer because I couldn't find a proper reason as to what to use from all of these options. The answer to the question is option 3 (Splice()).
1) SHIFT() - Remove First Element from Original Array and Return the First Element
See reference for Array.prototype.shift(). Use this only if you want to remove the first element, and only if you are okay with changing the original array.
const array1 = [1, 2, 3];
const firstElement = array1.shift();
console.log(array1);
// expected output: Array [2, 3]
console.log(firstElement);
// expected output: 1
2) SLICE() - Returns a Copy of the Array, Separated by a Begin Index and an End Index
See reference for Array.prototype.slice(). You cannot remove a specific element from this option. You can take only slice the existing array and get a continuous portion of the array. It's like cutting the array from the indexes you specify. The original array does not get affected.
const animals = ['ant', 'bison', 'camel', 'duck', 'elephant'];
console.log(animals.slice(2));
// expected output: Array ["camel", "duck", "elephant"]
console.log(animals.slice(2, 4));
// expected output: Array ["camel", "duck"]
console.log(animals.slice(1, 5));
// expected output: Array ["bison", "camel", "duck", "elephant"]
3) SPLICE() - Change Contents of Array by Removing or Replacing Elements at Specific Indexes.
See reference for Array.prototype.splice(). The splice() method changes the contents of an array by removing or replacing existing elements and/or adding new elements in place. Returns updated array. Original array gets updated.
const months = ['Jan', 'March', 'April', 'June'];
months.splice(1, 0, 'Feb');
// inserts at index 1
console.log(months);
// expected output: Array ["Jan", "Feb", "March", "April", "June"]
months.splice(4, 1, 'May');
// replaces 1 element at index 4
console.log(months);
// expected output: Array ["Jan", "Feb", "March", "April", "May"]
You can use the ES6 Destructuring Assignment feature with a rest operator. A comma indicates where you want to remove the element and the rest (...arr) operator to give you the remaining elements of the array.
const source = [1,2,3,5,6];
function removeFirst(list) {
var [, ...arr] = list;
return arr;
}
const arr = removeFirst(source);
console.log(arr); // [2, 3, 5, 6]
console.log(source); // [1, 2, 3, 5, 6]
Typescript solution that does not mutate original array
function removeElementAtIndex<T>(input: T[], index: number) {
return input.slice(0, index).concat(input.slice(index + 1));
}
You can also do this with reduce:
let arr = [1, 2, 3]
arr.reduce((xs, x, index) => {
if (index == 0) {
return xs
} else {
return xs.concat(x)
}
}, Array())
// Or if you like a oneliner
arr.reduce((xs, x, index) => index == 0 ? xs : xs.concat(x), Array())
Array.splice() has the interesting property that one cannot use it to remove the first element. So, we need to resort to
function removeAnElement( array, index ) {
index--;
if ( index === -1 ) {
return array.shift();
} else {
return array.splice( index, 1 );
}
}

Categories

Resources