Programmatically modifying maps based on inner condition - javascript

I have a map like this for example
const Map = new Map().set('123', [ [ 'foo', 'bar' ] ]).set('456', [ [ 'baz', 'qux' ], [ 'quux', 'corge' ] ]);
/*
The structure of the Map looks like this:
Map {
'123' => [ [ 'foo', 'bar' ] ],
'456' => [ [ 'baz', 'qux' ], [ 'quux', 'corge' ] ]
}
*/
How would I go about deleting the array where the first nested element in the array === 'quux' so that it would return this?
Map {
'123' => [ [ 'foo', 'bar' ] ],
'456' => [ [ 'baz', 'qux' ] ]
}
I know how to remove the item by doing
Map.set('456', (Map.get('456')).filter(array => array[0] !== 'quux'));
But this is only because I know which key ('456') has the element with 'quux' in it. I'm not sure how I would programmatically sweep through the Map then find the corresponding key and then remove the item. The keys and values in the Map will dynamic (but the structure will be the same), whereas the element to search for will be static, i.e: 'quux', what I mean by this is that the contents in the Map could vary, and I am simply performing a search and remove.

You could iterate the map and if the wanted value is found, filter the array and assign the filtered array.
const map = new Map([['123', [['foo', 'bar']]], ['456', [['baz', 'qux'], ['quux', 'corge']]]]);
map.forEach((v, k, m) => {
if (v.some(a => a[0] === 'quux')) {
m.set(k, v.filter(a => a[0] !== 'quux'));
}
});
console.log([...map]);

You can loop over the values of the Map, use findIndex on each value v to see if it includes an array whose first element is quux, and splice that array out if so:
const map = new Map().set('123', [ [ 'foo', 'bar' ] ]).set('456', [ [ 'baz', 'qux' ], [ 'quux', 'corge' ] ]);
console.log("before", [...map]);
for (const v of map.values()) {
const index = v.findIndex((a) => a[0] === "quux");
if (index > -1) {
v.splice(index, 1);
}
}
console.log("after", [...map]);
Here’s the non-destructive alternative, which creates a new Map by taking the entries of the old one and mapping the values to filter out the arrays we don’t want:
const before = new Map().set('123', [ [ 'foo', 'bar' ] ]).set('456', [ [ 'baz', 'qux' ], [ 'quux', 'corge' ] ]);
console.log("before", [...before]);
const after = new Map([...before].map(([k, v]) => {
return [k, v.filter((a) => a[0] !== "quux")];
}))
console.log("after", [...after]);
NOTE: One difference between the two approaches is that the second one will remove all arrays that have quux as their first element, whereas the second one will remove only the first such array. They can, of course, both be altered to fit whichever of the two options you need.

You could do the key dynamically with a for of loop like this:
BTW open your devtools to checkout the new map since map cannot be properly displayed in the code snippet.
const Map = new Map().set('123', [
['foo', 'bar']
]).set('456', [
['baz', 'qux'],
['quux', 'corge']
]);
for (let el of Map) {
Map.set(el[0], (Map.get(el[0])).filter(array => array[0] !== 'quux'));
}
console.log(Map);
I hope this is what you wanted and otherwise you can comment and I will have a look at it ;).

Iterate over key-value pair of the map, the value will have the outer array from which we can filter out the inner array having the value we are looking for. We can get the index of the inner array from the forEach function, using which we can use the splice function to remove the inner array from the outer array.
const map = new Map().set('123', [ [ 'foo', 'bar' ] ]).set('456', [ [ 'baz', 'qux' ], [ 'quux', 'corge' ] ]);
map.forEach((v, k)=>
{
v.forEach((arr, idx)=> {
if(arr.includes('quux')){
v.splice(idx,1);
}
},)
});
console.log(map);

Not sure if it's better from the performance point to always use Array.prototype.filter or use Array.prototype.some before filtering the array.
This solution just filters all arrays without checking an apperance of 'quux' before.
const map = new Map().set('123', [ ['foo', 'bar' ] ]).set('456', [ [ 'baz', 'qux' ], [ 'quux', 'corge' ] ]);
map.forEach((val, key) => {
val = val.filter(arr => arr[0] !== 'quux');
map.set(key, val);
});
console.log(map);

Related

How to convert objects inside array to nested array JavaScript?

I have an array containing objects that every element but the last one are objects, but I want to convert them into an array of arrays and add the last element.
To be more explicit here is how I have it:
[
{ 0: [1,2], 1: [6,2], name: "" },
{ 0: [3,4], 1: [2,2], name: "" }
]
and the result I want is this one:
[
{ multipolygon: [ [1,2], [6,2] ], name: ""},
{ multipolygon: [ [3,4], [2,2] ], name: ""}
]
Each single array contained inside the original array is converted into an array of those arrays.
I have tried doing this:
const zonesArray = zones.map(el => Object.values(el)) // obj => array
const polygons = zonesArray.filter(el => el.pop()) // array without name
to get all the arrays contained inside the obj but then I realized how can I replace this into the original objects.
I have tried to modify the groupBy function found on MDN:
function groupBy(objectArray, property) {
return objectArray.reduce(function (acc, obj) {
let key = obj[property]
if (!acc[key]) {
acc[key] = []
}
acc[key].push(obj)
return acc
}, {})
}
But I can't seem to find the answer
It doesn't look like you're trying to group by a property, you're trying to transform each object in the array separately - which can be done by taking the name and the numeric properties together when mapping, then returning the shape of the new object:
const arr = [
{ 0: [1,2], 1: [6,2], name: "" },
{ 0: [3,4], 1: [2,2], name: "" }
];
const result = arr.map(({ name, ...rest }) => ({
name,
multipolygon: Object.values(rest)
}));
console.log(result);

Convert multidimensional array to objects using dynamically generated and predefined array of items I want to set as keys

Are the steps I'm taking to solve this problem correct?
I'm working on turning a data structure of an array of arrays such as
this.arrayofAnimalsAndValues = [
[ "Ant", 1287, 12956],
[ "Lion", 2574, 25826],
[ "Bear", 3861, 38696],
.....
]
into this
this.jsonOfAnimalsAndValues = [
{category: "Ant", value_1: 1287, value_2:12956},
{category: "Lion", value_1: 2574, value_2:25826},
{category: "Bear", value_1: 3861, value_2:38696},
.....
]
where the first item in the array is always assigned to 'category' in the array object and the following items assigned to value_# depending on their order in the array. So the 2nd array item would have key value_1 and so on. For example, for 1 nested array:
[[ "Ant", 5148, 51566]] to =>
[{category: "Ant", value_1: "5148", value_2: 51566}]
I've created a hardcoded way to achieve this however I'm trying to make it dynamic:
'hardcoded' way:
this.variableOfKeys = ["value_1", "value_2", "value_3", ......]
this.jsonOfAnimalsAndValues = this.arrayofAnimalsAndValues(function(x) {
return {
category: x[0],
no1: x[1],
no2: x[2],
.....
};
});
where I just hardcode the keys and their values (values using their index).
My attempt to make it dynamic:
this.variableOfKeys.forEach(element => {
this.jsonOfAnimalsAndValues = this.arrayofAnimalsAndValues.map(function(x) {
for (var i = 0; i<=this.arrayOfValuesToUseAsKeys.length; ++i) {
return {
category: x[0],
element: x[i+1],
};
}
});
});
where my logic is that for each item in
this.variableOfKeys = ["value_1", "value_2", "value_3", ......],
I created this.jsonOfAnimalsAndValues such that the first item (item with the 0th index) in the array this.arrayofAnimalsAndValues is assigned to the key category and the following items (depending on their index) are assigned to the values in this.variableOfKeys in order starting from no1, then no2 etc.
However, I don't think this is written correctly and I keep getting this error:
"TypeError: Cannot read property 'variableOfKeys' of undefined"
Can I ask how it might be written incorrectly and so how I might be able to create this.jsonOfAnimalsAndValues from this.arrayofAnimalsAndValues?
You can map each subarray to an array of entries, then turn it into an object to return with Object.fromEntries:
const arrayofAnimalsAndValues = [
[ "Ant", 1287, 12956],
[ "Lion", 2574, 25826],
[ "Bear", 3861, 38696],
];
const output = arrayofAnimalsAndValues.map(
([category, ...rest]) => Object.fromEntries([
['category', category],
...rest.map((value, i) => ['value_' + (i + 1), value])
])
);
console.log(output);
Given an array of the keys you can map() the main array and use reduce() on each subarray to generate each object
const arr=[["Ant",1287,12956],["Lion",2574,25826],["Bear",3861,38696]],
keys = ['category','value_1','value_2'];
const res = arr.map(e => e.reduce((a,c,i) => (a[keys[i]] = c, a),{}))
console.log(res)
.as-console-wrapper { max-height: 100%!important;top:0}

How to get a specific array from a list of arrays

I want to get a specific array based on a code, the array is something like this:
const arr = [
[
"https://firebasestorage.googleapis.com/v0/b/ziro-a…=media&token=11f18ac1-0476-4a1e-ada6-09e6566abc19",
1595619171842,
"0b7ad06f-7776-4bab-a8c6-53fd5fd5bd9b"
],
[
"https://firebasestorage.googleapis.com/v0/b/ziro-a…=media&token=b64c143d-e817-434f-bf6f-0bd0e8d9e7b5",
1595619171844,
"2f44a130-71d9-47ce-b5d5-04587c3c81fc"
],
[
"https://firebasestorage.googleapis.com/v0/b/ziro-a…=media&token=71dc5d26-75f4-4141-905e-074b0705eac4",
1595619171845,
"d7eb2a05-1f5a-48dd-b7ac-f3b071499d00"
],
[
"https://firebasestorage.googleapis.com/v0/b/ziro-a…=media&token=d3645614-0ea3-4d17-80ab-57c6c6525fab",
1595619171846,
"940fb9a7-6fdd-4f8b-a808-26a9c60114bf"
]
];
How to I get the array with code "d7eb2a05-1f5a-48dd-b7ac-f3b071499d00"?
I was using reduce to get the more recent image, but now I have no idea!
Array#find returns the first elmenent of an array that returns true for the given function.
const specificArray = arr.find(subArray => {
return subArray[2] === "d7eb2a05-1f5a-48dd-b7ac-f3b071499d00";
}
Array#find (with destructuring) is best suited for this purpose.
const res = arr.find(([,,code])=>code==="d7eb2a05-1f5a-48dd-b7ac-f3b071499d00");
const arr = [
[
"https://firebasestorage.googleapis.com/v0/b/ziro-a…=media&token=11f18ac1-0476-4a1e-ada6-09e6566abc19",
1595619171842,
"0b7ad06f-7776-4bab-a8c6-53fd5fd5bd9b"
],
[
"https://firebasestorage.googleapis.com/v0/b/ziro-a…=media&token=b64c143d-e817-434f-bf6f-0bd0e8d9e7b5",
1595619171844,
"2f44a130-71d9-47ce-b5d5-04587c3c81fc"
],
[
"https://firebasestorage.googleapis.com/v0/b/ziro-a…=media&token=71dc5d26-75f4-4141-905e-074b0705eac4",
1595619171845,
"d7eb2a05-1f5a-48dd-b7ac-f3b071499d00"
],
[
"https://firebasestorage.googleapis.com/v0/b/ziro-a…=media&token=d3645614-0ea3-4d17-80ab-57c6c6525fab",
1595619171846,
"940fb9a7-6fdd-4f8b-a808-26a9c60114bf"
]
];
const res = arr.find(([,,code])=>code==="d7eb2a05-1f5a-48dd-b7ac-f3b071499d00");
console.log(res);

Convert array of objects to grouped object with values in lodash

I’m looking for a compact lodash solution to take an array of objects and then create a new object with the keys of the objects in the array and the unique values for each key.
[
{
color: "black",
type: "bag",
},
{
color: "red",
type: "pants",
},
{
color: "black",
type: "jacket",
},
]
Desired output:
{
color: ["black", "red"],
type: ["bag", "pants", "jacket"],
}
Thanks!
You can solve this using lodash#flatMap with a lodash#toPairs iteratee, to create a flattened version of an array pair of keys and values. Use lodash#groupBy with an iteratee function that removes the key of the array pairs, Array#shift, and use such value as the grouping key for the collection. Lastly, use lodash#mapValues with an iteratee that is composed of lodash#flatten and lodash#uniq, to flatten the array and only get the unique values for each grouped array. Note that the function composition uses lodash#flow to compose successive function arguments to form one functional result.
var result = _(array)
.flatMap(_.toPairs)
.groupBy(v => v.shift())
.mapValues(_.flow(_.flatten, _.uniq))
.value();
var array = [
{
color: "black",
type: "bag",
},
{
color: "red",
type: "pants",
},
{
color: "black",
type: "jacket",
}
];
var result = _(array)
.flatMap(_.toPairs)
.groupBy(v => v.shift())
.mapValues(_.flow(_.flatten, _.uniq))
.value();
console.log(result);
.as-console-wrapper{min-height:100%;top:0}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
I can't see a function that'll do this for you, so here's a solution that solves the problem in a number of straightforward steps using only lodash functions.
const listOfPairs = _.flatten(_.map(input, _.toPairs))
transforms the list of objects into a list of pairs of [key, value]
listOfPairs = [
[ 'color', 'black' ],
[ 'type', 'bag' ],
[ 'color', 'red' ],
[ 'type', 'pants' ] ]
now, we can group these up by the values in the first position in each pair.
const indexByKeyToValues = _.toPairs(_.groupBy(listOfPairs, _.head))
which gives us
indexByKeyToValues = [
[ 'color', [ ['color', 'black'], ['color', 'red'] ] ],
[ 'type', [ ['type', 'bag'], ['type', 'pants'] ] ] ]
then, map over the value arrays to pick the last element (the original values in the input maps)
const pairsOfKeyAndValue = _.map(indexByKeyToValues, ([k, vs]) => [k, _.map(vs, _.last)])
which is almost there
pairsOfKeyAndValue = [
[ 'color', [ 'black', 'red' ] ],
[ 'type', [ 'bag', 'pants' ] ] ]
we just need to rebuild an object using these pairs
const result = _.fromPairs(pairsOfKeyAndValue)
The whole "transforming a map to and from sequences of pairs" trick is really common in functional programming to do this kind of processing. Once you've done that, figuring out the rest of the steps isn't too tricky.
Hopefully this gives you a general idea you can use to solve these kinds of problems in future.
Use what's built-in. It's faster, shorter, and easier to reason from.
const arr = [{color: "black",type: "bag",},{color: "red",type: "pants",},{color: "black",type:"jacket",}],
obj = arr.reduce((h, y) => {
Object.keys(y).forEach(k => {
if (!h[k]) {
h[k] = []
h[k].push(y[k])
} else if (!h[k].includes(y[k])) h[k].push(y[k])
})
return h
}, {})
console.log(obj)

Deep key structure based on recursion

I've been using lodash for a while now and I really love the _.set and _.get methods.
I'm trying to solve a problem to get the deep key paths whose final value is a string, but when I'm too dumb for it. Spend 3 hours on it and can't find a perfect solution:
const myObject = {
a: 'myObject.a',
b: {
ba: 'myObject.b.ba',
bb: ['myObject.b.bb[0]'],
},
c: [
{ ca: 'myObject.c[0].ca' },
],
};
So I have myObject (that's far more nested in real life) and I want to get paths to the values, but only the final one.
The method would look like getDeepPaths(myObject) and would return in this case: ['myObject.a', 'myObject.b.ba', 'myObject.b.bb[0]', 'myObject.c[0].ca' ]
Did anyone solve something like this before?
Recursion is actually not that hard. Here's how you could solve this problem:
const myObject = {
a: 'myObject.a',
b: {
ba: 'myObject.b.ba',
bb: ['myObject.b.bb[0]'],
},
c: [
{ ca: 'myObject.c[0].ca' },
],
};
var stringLeaves = function(path, obj) {
if (typeof obj === 'string') {
return [path]
}
return Object.keys(obj)
.filter(k => obj.hasOwnProperty(k))
.map(k => stringLeaves(path + '.' + k, obj[k]))
.reduce((a,x) => a.concat(x), []); // this line flattens the array
};
console.log(stringLeaves('myObject', myObject));
The work is done by the stringLeaves function. In this function:
if the obj passed in as a parameter is a string, then just return the current path.
otherwise we assume that the object is an array, or a generic object, in which case we iterate through its properties:
for each property, call stringLeaves recursively, by passing in the adjusted path (current path + the new property name) and the object/value that resides at that particular key.
The convention of the function is that it returns an array of all possible matches. This is why:
for scalar string values I return an array (to keep things consistent)
I have the .reduce((a,x) => a.concat(x), []); line: to transform an array of arrays into one array that consists of all the values present in the original arrays.
Note that the function cannot deduce that your object is called myObject, so I passed that name as an initial path.
I'll provide a more generic solution that doesn't use lodash or other external dependencies
const traverse = function* (node, path = [])
{
if (Object (node) === node)
for (const [ key, value ] of Object.entries (node))
yield* traverse (value, [ ...path, key ])
else
yield [ path, node ]
}
We can easily step thru our data using a for loop. Notice the generator yields a path-value pair for each value in the original object. All primitive values are included in the output, not just strings this time
// add a non-string value for demo
const myObject = {
...
d: 1
};
for (const [ path, value ] of traverse (myObject)) {
console.log ('path', path)
console.log ('value', value)
console.log ('---')
}
// path [ 'a' ]
// value myObject.a
// ---
// path [ 'b', 'ba' ]
// value myObject.b.ba
// ---
// path [ 'b', 'bb', '0' ]
// value myObject.b.bb[0]
// ---
// path [ 'c', '0', 'ca' ]
// value myObject.c[0].ca
// ---
// path [ 'd' ]
// value 1
// ---
If we wanted to, we can collect all of the pairs using Array.from
Array.from (traverse (myObject))
// [ [ [ 'a' ], 'myObject.a' ]
// , [ [ 'b', 'ba' ], 'myObject.b.ba' ]
// , [ [ 'b', 'bb', '0' ], 'myObject.b.bb[0]' ]
// , [ [ 'c', '0', 'ca' ], 'myObject.c[0].ca' ]
// , [ [ 'd' ], 1 ]
// ]
As you may have noticed, I keep path as an array rather than making it a .-separated string. There's no need to make it into a string just to split it apart again later.
const lookup = (obj, [ key, ...path ]) =>
obj && key
? lookup (obj [key], path)
: obj
for (const [ path, value ] of traverse (myObject)) {
console.log ('path', path)
console.log ('value', value)
console.log ('lookup', lookup (myObject, path))
console.log ('---')
}
// path [ 'a' ]
// value myObject.a
// lookup myObject.a
// ---
// path [ 'b', 'ba' ]
// value myObject.b.ba
// lookup myObject.b.ba
// ---
// path [ 'b', 'bb', '0' ]
// value myObject.b.bb[0]
// lookup myObject.b.bb[0]
// ---
// path [ 'c', '0', 'ca' ]
// value myObject.c[0].ca
// lookup myObject.c[0].ca
// ---
// path [ 'd' ]
// value 1
// lookup 1
// ---

Categories