Javascript map through array to make another array of objects - javascript

I want to go through an array of strings, and depending on what the string is, make an array of objects.
For example, if the array is:
[a,a,a,b,b,c,d]
I want to map through the array and make an object with key and value pairs that add up the strings consecutively:
[{a:1},{a:2},{a:3},{b:1},{b:2},{c:1},{d:1}]
How do I do this?
I've tried mapping through, but I can't get how to add on to the previous object's value (a:1 -> a:2)

While mapping, you need to store a separate count of how many times each item has appeared, and increment the appropriate key each iteration. You might use a Map for this:
const input = ['a','a','a','b','b','c','d'];
const map = new Map();
console.log(
input.map(char => {
const count = (map.get(char) || 0) + 1;
map.set(char, count);
return { [char]: count };
})
)

Related

JS reduceRight is causing arrays to become objects, how can I fix the logic?

I have an json object:
json = {
"array": [80, 50]
}
A third party jsoneditor, returns a path variable as a list of strings or index to the value. I use event listeners to detect changes to the value and I store those changes in a separate object to later submit to a form.
For example, if I change 50 to 60, this is what is returned:
node.path = ["array", 1]
node.value = 60
I am using reduceRight() to normally map the changes to the object to the changed object, however when the value is an Array it is converting the array to an object.
let delta = node.path.reduceRight((obj, elem) => ({[elem]: obj}), node.value)
//returns
delta = {array: {1: 60}}
//instead of
delta = {array: [80, 60]}
How can I check the original json and if the field is an Array object, don't reduce this into an object but keep the original Array and update the Array with the changed value within the Array? I need to submit the entire array into the form.
EDIT:
I know that I can do some custom logic within the reduce to check the original json and get the unchanged part of the array. I just dont know how to achieve that
let delta = node.path.reduceRight( function (obj, elem) {
//some logic here to account for arrays and get original array list and change only slected index
else {
return {[elem]: obj}), node.value
}
});
EDIT:
Alternatively, how can can I get the nested keys from the node.path and find the original array and then just update the array index? The path can sometimes be nested sothe path is always going to be the Array-like structure.
//this does not work
let orig_key_val = json[node.path]
Thanks!
This seems to work, finally... I used a regular reduce() and check if the key is an Array if it is store my original Array into a tmp object and save that key. The next time the reduce() comes around, if the index is on the last path element then set the tmp[tmp_key][key] to my target value and return the adjusted for Array object instead.
I can see how this will not work for nested json objects but for now I dont have any... Unless someone can show me how to figure that one out this is my implementation for now.
let tmp = {};
let tmp_key;
let delta = node.path.reduce((val, key, index, array) => {
if (Array.isArray(json[key])) {
tmp[key] = json[key]
tmp_key = key;
} else if (Object.keys(tmp).length !== 0 && index === node.path.length-1) {
tmp[tmp_key][key] = node.value;
return tmp
} else
return {[key]: val}
}, node.value);

Array of objects and i want to return true if any key value pair in the object is duplicate

I am fairly new to JS, I am struggling with a problem where there is an array of objects and i want to return true if any key value pair in the object is duplicate
var lookupValues = [{"key":"xiomi","value":"phone","id":1},{"key":"samsung","value":"tab",id:2},{"key":"blackberry","value":"phone","id":3},{"key":"xiomi","value":"tab","id":4},{"key":"asus","value":"phone",id:5}]}
Since key: "Xiomi" came twice so function should return true
i tried using lodash functions
var uniqueLookup =_uniqBy(lookupValues,'key')
if(_.isEqual(uniqueLookup, lookup)) return true
works but i guess i am missing something
You are on the right track. Compare the length of the array to the array shortened by checking if the key is unique.
var lookupValues = [{"key":"xiomi","value":"phone","id":1},{"key":"samsung","value":"tab",id:2},{"key":"blackberry","value":"phone","id":3},{"key":"xiomi","value":"tab","id":4},{"key":"asus","value":"phone",id:5}];
console.log(_.uniqBy(lookupValues,"key").length !== lookupValues.length);
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.19/lodash.min.js"></script>
Here how you can do this in VanillaJs.
Set is a new data structure that JavaScript got in ES6. It’s a collection of unique values. We put into that the list of property values we get from using map(), which how we used it will return this array:
[xiomi,Samsung,blackberry,xiomi,asus]
Passing through Set, we’ll remove the duplicates i.e xiomi and at last ... is the spread operator, which will expand the Set values into an array.
Now if length of this new array is equal to your lookupValues.length then it has no duplicates else duplicates are present.
var lookupValues = [{"key":"xiomi","value":"phone","id":1},{"key":"samsung","value":"tab",id:2},{"key":"blackberry","value":"phone","id":3},{"key":"xiomi","value":"tab","id":4},{"key":"asus","value":"phone",id:5}]
const isDuplicate = (lookupValues) => !([...new Set(lookupValues.map(obj => obj.key))].length === lookupValues.length)
console.log(isDuplicate(lookupValues))
I made a vanilla Javascript code snippet with es6 Set object.
var lookupValues = [{"key":"xiomi","value":"phone","id":1},{"key":"samsung","value":"tab",id:2},{"key":"blackberry","value":"phone","id":3},{"key":"xiomi","value":"tab","id":4},{"key":"asus","value":"phone",id:5}]
const keys = lookupValues.map((v) => v.key)
const keySetSize = new Set(keys).size
const hasDuplicate = keys.length > keySetSize
console.log(hasDuplicate)

how to get the last 3 elements with specific name value with JMESPATH and javascript?

I have the below json data array:
[{"name":"Button1","date":"1596959802144"},{"name":"Button2","date":"1596959804238"},{"name":"Button3","date":"1596959809334"},{"name":"Button1","date":"1597000878135"},{"name":"Button2","date":"1597000896335"},{"name":"Button3","date":"1597000901536"},{"name":"Button2","date":"1597000904437"},{"name":"Button3","date":"1597000909535"},{"name":"Button1","date":"1597000912250"},{"name":"Button2","date":"1597000939937"},{"name":"Button3","date":"1597000957940"},{"name":"Button2","date":"1597000964640"},{"name":"Button1","date":"1597001005141"},{"name":"Button2","date":"1597001010240"},{"name":"Button3","date":"1597001014845"},{"name":"Button2","date":"1597001021644"},{"name":"Button1","date":"1597001025738"},{"name":"Button2","date":"1597001049030"},{"name":"Button3","date":"1597001054139"},{"name":"Button1","date":"1597001057741"},{"name":"Button2","date":"1597001060340"},{"name":"Button3","date":"1597001062445"},{"name":"Button1","date":"1597002599045"},{"name":"Button1","date":"1597002604128"},{"name":"Button1","date":"1597002609546"},{"name":"Button1","date":"1597002613435"},{"name":"Button1","date":"1597002681736"},{"name":"Button1","date":"1597002690843"},{"name":"Button1","date":"1597002694136"},{"name":"Button1","date":"1597002696349"},{"name":"Button1","date":"1597002699243"}]
and I would like to use JMESPath javascript library to get only the last 3 entries per each distinct name value. For example:
[{"name":"Button3","date":"1597001014845"},{"name":"Button2","date":"1597001021644"},{"name":"Button2","date":"1597001049030"},{"name":"Button3","date":"1597001054139"},{"name":"Button2","date":"1597001060340"},{"name":"Button3","date":"1597001062445"},{"name":"Button1","date":"1597002694136"},{"name":"Button1","date":"1597002696349"},{"name":"Button1","date":"1597002699243"}]
So the last 3 occurrences fro each name = Button*
checking on stackOverflow and I saw that with JQ is possible to do using this function: map_values(delpaths(keys_unsorted[:-2] | map([.])))
Get last N elements for each item of a JSON object
Is there any way to do? or using other javascript module?
If you don't care about the order in your resulting array, here would be a pure JavaScript way to do this:
const getLastNForEveryName = (arr, n) => {
const lastNOfEach = arr.reduce((acc, curr) => {
if(acc[curr.name] == null) { // If the key doesnt exist yet, create it with the current item in the array
acc[curr.name] = [curr];
} else {
if(acc[curr.name].length >= n) // If the array is as big as the desired size alread, remove the first added one
acc[curr.name].shift();
acc[curr.name].push(curr); // push the current item in the array
}
return acc;
}, {})
return Object.values(lastNOfEach).flatMap(l => l); // Just get the values of the object and flatMap it, so that we dont have arrays of arrays
}
// Testing
const values = [{"name":"Button1","date":"1596959802144"},{"name":"Button2","date":"1596959804238"},{"name":"Button3","date":"1596959809334"},{"name":"Button1","date":"1597000878135"},{"name":"Button2","date":"1597000896335"},{"name":"Button3","date":"1597000901536"},{"name":"Button2","date":"1597000904437"},{"name":"Button3","date":"1597000909535"},{"name":"Button1","date":"1597000912250"},{"name":"Button2","date":"1597000939937"},{"name":"Button3","date":"1597000957940"},{"name":"Button2","date":"1597000964640"},{"name":"Button1","date":"1597001005141"},{"name":"Button2","date":"1597001010240"},{"name":"Button3","date":"1597001014845"},{"name":"Button2","date":"1597001021644"},{"name":"Button1","date":"1597001025738"},{"name":"Button2","date":"1597001049030"},{"name":"Button3","date":"1597001054139"},{"name":"Button1","date":"1597001057741"},{"name":"Button2","date":"1597001060340"},{"name":"Button3","date":"1597001062445"},{"name":"Button1","date":"1597002599045"},{"name":"Button1","date":"1597002604128"},{"name":"Button1","date":"1597002609546"},{"name":"Button1","date":"1597002613435"},{"name":"Button1","date":"1597002681736"},{"name":"Button1","date":"1597002690843"},{"name":"Button1","date":"1597002694136"},{"name":"Button1","date":"1597002696349"},{"name":"Button1","date":"1597002699243"}];
console.log(getLastNForEveryName(values, 3));

how to add different array item values into single array element?

i have an array like [x/0/2 , x/0/3 , y/3/1 , x/1/1 , x/0/3 , x/1/2],
i need to convert the elements range like [x/0/2-3 , y/3/1 , x/1/1-2]
Please give some suggestion for this.
Use reduce to iterate over the array and create an object grouped by the element root, then use Object.entries to pull out the correct information from the object.
const arr = ['x/0/2', 'x/0/3', 'y/3/1', 'x/1/1', 'x/0/3', 'x/1/2'];
const out = arr.reduce((acc, c) => {
// `split` out the separate parts of the element
const [ root1, root2, index ] = c.split('/');
// We'll use the first two parts as the object key
const key = `${root1}/${root2}`;
// If the key doesn't already exist create an empty
// array as its values
acc[key] = acc[key] || [];
// To prevent duplicates only add an index if it
// isn't already in the array
if (!acc[key].includes(index)) acc[key].push(index);
// Return the accumulator for the next iteration
return acc;
}, {});
// Then iterate over the object entries with `map`
const result = Object.entries(out).map(([ key, values ]) => {
// Return the joined up value
return `${key}/${values.join('-')}`;
});
console.log(result);
If I understand your question, you could create an array within the array to hold the range of values. Checking if the position in the array is an actual array let’s you know there are values that span a range within.
Example:
var values = [x/01, [x/20, x/21, x/22], x/03]
You could also create an object that could accomplish something similar depending on your needs.

Is it possible to use array iteration methods on ES6 Set instances?

I am using ES6 Set instances and I need to apply some transformations on them. These are transformations of the kind that would be simple if they were arrays. Here is an example:
let s = new Set;
s.add(1);
s.add(2);
s.add(3);
let n = s.filter(val => val > 1); // TypeError, filter not defined
let n = Array.prototype.filter.call(s, val => val > 1); // []
I was hoping that the result would either be a new Set or an array. I similarly want to use other array comprehension methods like filter, map, reduce, etc. And I would also like to have similar behaviour on ES6 Map instances as well.
Is this possible, or do I need to be using vanilla JS arrays?
you can get the values of s in an array using
Array.from(s.values())
Array.from documentation states that it creates a new Array instance from an array-like or iterable object.
Set.values returns a new Iterator object that contains the values for each element in the Set object in insertion order.
So your code becomes
let s = new Set;
s.add(1);
s.add(2);
s.add(3);
let n = Array.from(s.values()).filter(val => val > 1)
You can't use Array methods directly on a Set or Map object. Array methods expect .length and [n] indexing which is not how Set or Map work.
You can either convert your Set to an array using Array.from(s) or you can create your own methods to operate directly on the Set or Map. If you're going to be doing this a lot and the desired end result is a Set or Map, then it's probably better to not convert to an Array, modify, then convert back. Plus, converting a Map to an array is not quite so simple since you have both a key and value (might have to be an array of objects).
For example, you could create your own .filter() method for a Set object like this:
Set.prototype.filter = function(fn) {
let result = new Set();
for (let val of this) {
if (fn(val, this) === true) {
result.add(val);
}
}
return result;
}
let s = new Set;
s.add(1);
s.add(2);
s.add(3);
let n = s.filter(val => val > 1);
// log the output
// log output
document.write("Set {" + Array.from(n).join(", ") +"}");
Similar methods could be created for other Array methods.

Categories