I have a dataset that looks like this:
[
[a, 10],
[b, 20],
[c, 30],
[d, 40],
["err", NaN],
[a, "foo"],
[b, "foo2"],
[c, "foo3"],
[f, "foo4"]
]
I want to map the first object of each array, if it's repeated set it like this and if it wasn't shown earlier fill with nulls:
[a, 10, "foo"],
[b, 20, "foo2"],
[c, 30, "foo3"],
[d, 40, null ],
[f, null, "foo4"]
I'm using lodash but I'm noob.
Any idea how to get closer is really appreciated.
Using lodash
Start a lodash's chain. Collect arrays to a group by their 1st index using _.groupBy(). Iterate the groups with _.map(), init an item that includes the key (0 index), and nulls. Iterate the group items, and take the 2nd value of the array. Insert the value in the correct place by checking if it's a number (_.isNumber):
var arr = [["a",10],["b",20],["c",30],["d",40],["err",null],["a","foo"],["b","foo2"],["c","foo3"],["f","foo4"]];
var result = _(arr) // start a chain
.groupBy('0') // group by index 0
.map(function(group, key) { // map the groups
var item = [key, null, null]; // init an item
// iterate the group
group.forEach(function(s) {
item[_.isNumber(s[1]) ? 1 : 2] = s[1]; // add the number or the string to the right place
});
return item;
})
.value(); // get the chain value
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
Using ES6
Reduce the array to an ES6 Map. For each item, check it it exists in the map, and if not, add if not init an item that includes the key (0 index), and nulls. Place the value in index 1 if it's a number or 2 if it's not. Get the map values iterator, and spread back to an array:
const arr = [["a",10],["b",20],["c",30],["d",40],["err",null],["a","foo"],["b","foo2"],["c","foo3"],["f","foo4"]];
const result = [...arr.reduce((m, [key, value]) => {
// if item doesn't exist in the Map, create a new item
m.has(key) || m.set(key, [key, null, null]);
// get the item from the map, and set the new value in the right place
m.get(key)[typeof value === 'number' ? 1 : 2] = value;
return m;
}, new Map()).values()]; // get the Map's value iterator and spread to an array
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
You can do this with Array.prototype.reduce. You'll need to get the first element of the array and, if it's the first time you've encountered it, store it in an index along with its position in the result array. Then add a copy of the array to the result array.
If you've seen the index before, just append the value to the result array. E.g.
// First elements are changed to strings for simplicity
var data = [
['a', 10],
['b', 20],
['c', 30],
['d', 40],
['err', NaN],
['a', "foo"],
['b', "foo2"],
['c', "foo3"],
['f', "foo4"]
];
var result = data.reduce(function(acc, arr){
// Use first element of array as a key. If not seen before, add
// to index object with its index in the data array.
// Then append a copy of the data array.
if (!acc.index.hasOwnProperty(arr[0])) {
acc.index[arr[0]] = acc.data.length;
acc.data.push(arr.slice());
// Otherwise, just append the value to appropriate array in data
} else {
acc.data[acc.index[arr[0]]].push(arr[1]);
}
return acc;
// Required output is in data property
}, {index:{}, data:[]}).data;
console.log(result);
I've changed the variables to strings for simplicity. Just make sure that each variable's value is unique when stringified and is suitable as an object property, otherwise you'll need to use a Map.
This could be somewhat more concise, at the risk of obfuscation.
Related
If I normally use for of loop and use iterator as entries that situation look like this:
var a = ['a', 'b', 'c'];
var iterator = a.entries();
for (let e of iterator) {
console.log(e);
}
// [0, 'a']
// [1, 'b']
// [2, 'c']
iterator: will be the entire array that contain all elements key/value pair. Key will be the index.
e: will be an element in the array
BUT what is this??????
let text = "A A A";
let splitwords = text.split(" ");
let words = [];
for (const [, item] of splitwords.entries()) {
words.push(item.split(""));
}
console.log(`This is the words: ${words}`);
what meaning the [, item] part???
and why should i use this pattern?
text.split("") do exactly same thing or not?
(Otherwise I try solve an text animation problem and this inherit from that code:
framer motion text animation )
Thank you
PS: I know this is an array destructing my main question that why????
for (const [, item] of splitwords.entries()) {
words.push(item.split(""));
}
[, item] is called array destructing, and the reason for that is because of entries in splitwords.entries(). The result of that array is like this [0, "a"],[1, "b"] (the first item is an index, and the second item is value), but in this case, they don't use an index, so the declaration is like [,item] (If they want to use it, the code can be [index, item]).
Without using an index, they can implement this way
for (const item of splitwords) { //remove entries and array destructing
words.push(item.split(""));
}
and why should I use this pattern?
Well, I think this is just their code style to use for of for collecting both index and value together. It depends on you need an index or not.
words.push(item.split("")) is different from the above case, they try to make the entire word "Hello" into characters ["H","e","l","l","o"] for the animation, so the final result of words can be
[
["F","r","a","m","e"],
["M","o","t","i","o","n"],
...
]
It's just a way to skip destructuring the first element in the array. You are basically saying that you are not interested in the first array item.
Since the entries() method returns a new Array Iterator object that contains the key/value pairs for each index in the array.
const foo = ['a', 'b', 'c'];
for (const [, item] of foo.entries()) {
// item would be 'a', 'b', 'c'
}
for (const [index, item] of foo.entries()) {
// item would be 'a', 'b', 'c'
// index would be 0, 1, 2
}
This is a destructuring assignment.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
Array.prototype.entries() returns an array of 2 elements for the index and the item itself for each array item.
So in this expression the index is assign to nothing (because there is no variable declared before the comma) and the item is assigned to item.
Example
const ar = [1,2]
const [,value2]=ar
console.log(value2)
const [value1,]=ar
console.log(value1)
const [v1, v2] = ar
console.log(v1, v2)
For the item.split("") it just seems useless, the result would be the same in the present case with just words.push(item) since each word is just one letter... If the words were more than one letter this would just store each letter separately in the words array. Which could maybe be called letters I guess...
Edit:
for the "why use this pattern" question. In the present case, again it just seems useless. The index is not used so calling entries just don't seems relevant.
I don't know if you 'should' use this pattern, but if you want an explanation of what it is, it's like
for(let temp of splitwords.entries()){
const [, item] = temp;
words.push(item.split('');
}
And then const [, item] = temp; is basically the same as const item = temp[1].
Due to how ".entries()" work for an array (giving [index, value]), since the index is ignored by the destructuring, the resulting item is just the value. So, I don't think the result is any different from the following:
for (const item of splitwords) {
words.push(item.split(""));
}
Maybe they were preparing for the future, in case the index becomes important some day.
'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);
I need to print the duplicates from an array.
For example, given the following array and index, the function will print 6,23,33,100.
var array = [3,6,67,6,23,11,100,8,93,0,17,24,7,1,33,45,28,33,23,12,99,100];
Apparently we need to do it using two 'while loops'.
Im looking for a solution, but more importantly an explanation.
Links are very much appreciated.
Thanks!
The most elegant and efficient solution is to use a while loop which iterates the array only once, so, we have O(N) complexity.
For this, we need to declare an hash which keeps the number of occurencies for each array's item. If we found a duplicate one then we store it in duplicates array.
var arr = [3,6,67,6,23,11,100,8,93,0,17,24,7,1,33,45,28,33,23,12,99,100], i = 0, hash = {}, duplicates = [];
while(i < arr.length){
hash[arr[i]] = hash[arr[i]] ? hash[arr[i]] += 1 : 1;
if (hash[arr[i]] === 2)
duplicates.push(arr[i])
i++;
}
console.log(duplicates)
You could use the filter() and indexOf() methods.
var array = [3,6,67,6,23,11,100,8,93,0,17,24,7,1,33,45,28,33,23,12,99,100];
console.log(array.filter((a,b,c) => c.indexOf(a) !== b));
a -> is the value being passed in the arrow function.
b -> is the index being passed in.
c -> is the whole array being passed in.
This line is filtering the array based on, if the original array has a value (argument a) whose index does not match the given index passed in through the arrow function (argument b).
A good sample and explanation can be found here... W3Resource
Futhermore to assist in understanding the two major components of the code the Object...
Working with objects - Javascipt
and Arrays...Javascipt Arrays
For the shortest approach, you could take a closure
(s => )( )
with a Set
(s => )(new Set)
and a check. If a value is already seen, then take this value
(s => v => s.has(v) )(new Set)
or add the value to the set and return false, because a not seen value should not in the result set.
(s => v => !s.add(v))(new Set)
var array = [3, 6, 67, 6, 23, 11, 100, 8, 93, 0, 17, 24, 7, 1, 33, 45, 28, 33, 23, 12, 99, 100],
duplicates = array.filter((s => v => s.has(v) || !s.add(v))(new Set));
console.log(duplicates);
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);
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);