intersecting multidimensional array in javascript - javascript

let say i have 1 multidimensional array and i want to exclude values that not equal in javascript.
here is the example array.
var filter = ["big_number", "odds_number"];
var arrays = {
"first" : {
"big_number" : [50,51,52],
"odds_number" : [39,41,51,53]
},
"second" : {
"big_number" : [61,62,63,64,65,70,72,73],
"odds_number" : [13,15,17,19,61,63,65,73]
}
};
i want to convert that array to be like this.
var new_arrays = {
"first" : [51],
"second" : [61,63,65,73]
};
here is my code
var newArray = {
"first" : [],
"second" : []
};
for (var k in arrays){
if (arrays.hasOwnProperty(k)) {
for(var f=0; f<filter.length; f++) {
newArray[k].push(arrays[k][filter[f]].filter(value => -1 !== arrays[k][filter[f]].indexOf(value))));
}
}
}
console.log(newArray);
actually i could do this code
var newArray = {
"first" : [],
"second" : []
};
for (var k in arrays){
if (arrays.hasOwnProperty(k)) {
newArray[k].push(arrays[k]["big_number"].filter(value => -1 !== arrays[k]["odds_number"].indexOf(value))));
}
}
console.log(newArray);
but i need to convert it through filter variable.
i could not use filter[0] and filter[1], because that values could change dynamically and could be more than 2 values in array.

You could loop through the keys and update the values using filter and includes:
var arrays={"first":{"big_number":[50,51,52],"odds_number":[39,41,51,53]},"second":{"big_number":[61,62,63,64,65,70,72,73],"odds_number":[13,15,17,19,61,63,65,73]}};
for (let key in arrays) {
arrays[key] = arrays[key]["big_number"]
.filter(n => arrays[key]["odds_number"].includes(n));
}
console.log(arrays)
If you don't want to mutate the original object then use Object.entries and reduce:
var arrays={"first":{"big_number":[50,51,52],"odds_number":[39,41,51,53]},"second":{"big_number":[61,62,63,64,65,70,72,73],"odds_number":[13,15,17,19,61,63,65,73]}};
const newObject = Object.entries(arrays).reduce((r, [key, {big_number, odds_number}]) => {
r[key] = big_number.filter(n => odds_number.includes(n));
return r
}, {})
console.log(newObject)
If you have more than 2 array properties, you can do something like this: Get all the arrays using Object.values and then use reduce to run the previous code recursively
var arrays = {
"first": {
"big_number": [50, 51, 52],
"odds_number": [39, 41, 51, 53],
"another_key": [41, 51, 53]
},
"second": {
"big_number": [61, 62, 63, 64, 65, 70, 72, 73],
"odds_number": [13, 15, 17, 19, 61, 63, 65, 73],
"another_key": [63, 65]
}
};
for (let key in arrays) {
arrays[key] = Object.values(arrays[key])
.reduce((a, b) => a.filter(c => b.includes(c)))
}
console.log(arrays)

Here is a little intersection snippet:
function intersect(a,b){
b.slice()
return a.filter(item=>{
if(b.includes(item)){
b.splice(b.indexOf(item),1)
return true
}
})
}
Using that, you can do this easily:
function intersect(a,b){
b.slice()
return a.filter(item=>{
if(b.includes(item)){
b.splice(b.indexOf(item),1)
return true
}
})
}
var filter = ["big_number", "odds_number"];
var output={}
var arrays = {
"first" : {
"big_number" : [50,51,52],
"odds_number" : [39,41,51,53]
},
"second" : {
"big_number" : [61,62,63,64,65,70,72,73],
"odds_number" : [13,15,17,19,61,63,65,73]
}
};
for(x in arrays){
output[x]=arrays[x][filter[0]]
for(let i=1;i<filter.length;i++){
output[x]=intersect(output[x],arrays[x][filter[i]])
}
}
console.log (output)

use Object.entries to get keys and values and then use reduce
var arrays = {
"first" : {
"big_number" : [50,51,52],
"odds_number" : [39,41,51,53]
},
"second" : {
"big_number" : [61,62,63,64,65,70,72,73],
"odds_number" : [13,15,17,19,61,63,65,73]
}
};
const output =Object.entries(arrays).reduce((accu, [key, {big_number}]) => {
if(!accu[key]) accu[key] = [];
big_number.forEach(num => {
if(num%2 !==0)
accu[key].push(num);
})
return accu;
}, {});
console.log(output);

You can get the unique values from both the arrays using Set and then using filter get only the common values.
var arrays = {"first": {"big_number": [50, 51, 52],"odds_number": [39, 41, 51, 53]},"second": {"big_number": [61, 62, 63, 64, 65, 70, 72, 73],"odds_number": [13, 15, 17, 19, 61, 63, 65, 73]}},
result = Object.keys(arrays).reduce((r,k) => {
let setB = new Set(arrays[k]["big_number"]);
r[k] = [...new Set(arrays[k]["odds_number"])].filter(x => setB.has(x));
return r;
},{});
console.log(result)
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

How to get the min, max, and sum of JSON data

I had JSON data that came back with single int values. With some changes, the values are now coming back as arrays of ints (as well as the original format).
{
"value": 10,
"value": 70,
"value": 30,
"value": 200
}
- and -
{
"value": [64, 13, 55, 34, 52, 43, 59, 20, 20],
"value": [10, 90, 20, 80, 30, 70, 60, 40, 50]
}
I had a formula that would return the min, max, and sum of the old version of JSON data. Now it doesn't work, and I can't figure out what would be the best way to re-write the function to handle the arrays. Or if its better to make a second function to handle just arrays and do a check if it is an int or array?
Is there a way that would return (from the numbers above):
// no value array, apply to all
[ 10, 200, 310 ] // min, max, sum
- and -
// for each of the value arrays
[ 23, 64, 360 ] // val 1 - min, max, sum
[ 10, 90, 450 ] // val 2 - min, max, sum
// input data
const value = document.querySelectorAll( "div" ).forEach( el => {
const contents = el.textContent, // get the text in the <div>
json = JSON.parse( contents ), // parse the data
jsonData = json.data; // get the data only
// normalise the data
// #from: https://stackoverflow.com/a/67294607/1086990
const normaliseData = arr => {
const data = arr.map(({ value }) => value);
return typeof arr[0].value === 'number' ? [data] : data;
};
// add into const
const valueArray = normaliseData( jsonData );
// get the min / max / sum
const minMaxSum = valueArray.forEach( e => {
return [
Math.min(...e),
Math.max(...e),
[...e].reduce((v, w) => v + w)
];
});
// output
console.log( minMaxSum );
});
<div>
{ "data": [ { "value": [64, 23, 45, 34, 52, 43, 59, 40] }, { "value": [10, 90, 20, 80, 30, 70, 60, 40, 50] } ] }
</div>
<div>
{ "data": [ { "value": 600 }, { "value": 70 }, { "value": 30 } ] }
</div>
Normalize the data by testing the type of the value of the first object in each array:
const valueInArray = [{ value: [64, 23] }, { value: [45, 34] }];
const valueAsSingle = [{ value: 600 }, { value: 70 }];
const normalizeData = arr => {
const data = arr.map(({ value }) => value);
return typeof arr[0].value === 'number'
? [data]
: data;
};
console.log(normalizeData(valueInArray));
//=> [ [ 64, 23 ], [ 45, 34 ] ]
console.log(normalizeData(valueAsSingle));
//=> [ [ 600, 70 ] ]
Now they are the same shape, and so you can treat them equally.
You can use Math.max and Math.min to find the maximum and minimum of the array then assign the values in the specific variables.
Currently, when you are using val.value it is consisting of the whole array and hence you also need to iterate over the array to find the max, min, or sum.
To find the sum use reduce on the val.value array and then add it in the acc[2].
// input data
const valueInArray = document.getElementById("valueInArray").innerHTML,
valueAsSingle = document.getElementById("valueAsSingle").innerHTML;
// parse
const jsonArray = JSON.parse( valueInArray ),
jsonNumber = JSON.parse( valueAsSingle ),
jsonArrayData = jsonArray.data,
jsonNumberData = jsonNumber.data;
// get numbers
const minMaxSumArray = jsonArrayData.reduce( ( acc, val ) => {
// smallest number
acc[0] = (
( acc[0] === undefined || Math.min(...val.value) < acc[0] ) ?
Math.min(...val.value) : acc[0]
)
// largest number
acc[1] = (
( acc[1] === undefined || Math.max(...val.value) > acc[1] ) ?
Math.max(...val.value) : acc[1]
)
// sum of numbers
acc[2] = (
acc[2] === undefined ?
val.value.reduce((v, w) => v + w) : val.value.reduce((v, w) => v + w) + acc[2]
)
console.log('answer', acc)
// return the array
return acc;
}, [] );
<div id="valueInArray">
{ "data": [ { "value": [64, 23, 45, 34, 52, 43, 59, 40] }, { "value": [10, 90, 20, 80, 30, 70, 60, 40, 50] } ] }
</div>
<div id="valueAsSingle">
{ "data": [ { "value": 10 }, { "value": 70 }, { "value": 30 } ] }
</div>
My take on it: first, create a single Array of all values (either arrays or single) by concatening them and using Array.flat() to flatten it. Then use a reducer to determine the sum and use Math.min/max for the min and max values.
// input data
const valuesInArray = JSON.parse(
document.querySelector("#valueInArray").textContent).data;
const singleValues = JSON.parse(
document.querySelector("#valueAsSingle").textContent).data;
// get all values from the objects to a single Array of values
// (so: convert all to single values)
const allValues = valuesInArray.map( v => v.value )
.concat(singleValues.reduce( (acc, val) => [...acc, +val.value], [] ) )
.flat();
// let's see what we have
console.log(`All values from both objects: ${JSON.stringify(allValues)}`);
// create sum, min and max
const [ sum, min, max, ] = [
allValues.reduce( (a, v) => a + +v, 0),
Math.min(...allValues),
Math.max(...allValues) ];
console.log(`From all values sum is ${sum}, min ${min} and max ${max}`);
div {
display: none;
}
<div id="valueInArray">
{ "data": [
{ "value": [64, 23, 45, 34, 52, 43, 59, 40] },
{ "value": [10, 90, 20, 80, 30, 70, 60, 40, 50] } ]
}
</div>
<div id="valueAsSingle">
{ "data":
[ { "value": 10 }, { "value": 70 }, { "value": 30 } ]
}
</div>
The second snippet aggregates data per value, where the single values are added as a values array to valuesInArray.
// input data
const valuesInArray = JSON.parse(
document.querySelector("#valueInArray").textContent).data;
const singleValues = JSON.parse(
document.querySelector("#valueAsSingle").textContent).data;
// create sum, min and max *per value*, in one go
const aggregatesAdded = valuesInArray
.concat({ value: singleValues.reduce( (acc, val) => [...acc, +val.value], [] ) } )
.reduce( (acc, val) => [...acc, {...val, aggregatedValues: {
sum: val.value.reduce( (a, v) => a + +v, 0 ),
min: Math.min(...val.value),
max: Math.max(...val.value) } }
], [])
document.querySelector("pre").textContent =
JSON.stringify({data: aggregatesAdded}, null, 2);
div {
display: none;
}
<div id="valueInArray">
{ "data": [
{ "value": [64, 23, 45, 34, 52, 43, 59, 40] },
{ "value": [10, 90, 20, 80, 30, 70, 60, 40, 50] } ]
}
</div>
<div id="valueAsSingle">
{ "data":
[ { "value": 10 }, { "value": 70 }, { "value": 30 } ]
}
</div>
<pre id="result"></pre>

Get root property of an object using recursion

My algorithm works correctly when I apply fake objects to the function but on CodeWars it continues to fail. I'm very curious on what checks I'm missing in my code. I believe I have to use certain regular expressions but I'm very confused. Here's a link to the problem https://www.codewars.com/kata/get-root-property-name
function getRootProperty(object, val) {
var valueFound = false;
let output = '';
for (var first in object) {
var seachObject = object[first]
function query(object, val, rootName) {
Object.getOwnPropertyNames(object).forEach((value) => {
if (object[value] == val) {
valueFound = true;
output = rootName
return
} else {
query(object[value], val, rootName)
}
})
}
query(seachObject, val, first);
}
if (valueFound == false) {
return null
} else {
return output;
}
}
Luckily i only had to search new CodeWars problems and found it relatively fast: Problem description. Here is your issue: [...] other root properties may also have 9 buried in it but you should always return the first
Use if (!valueFound) query(object[value], val, rootName) for the first call (doesn't matter for the recursive calls).
The problem assumes an order in javascript properties which works in ecmascript8, partly in ecmascript6 and not at all in lower versions.
You may also do as follows;
function findRootProperty(o,x,p = this){
return Object.keys(o)
.reduce((r,k) => Array.isArray(o[k]) ? o[k].includes(x) ? r.concat(k)
: r
: r.concat(findRootProperty(o[k],x,k)),[])
.map(q => p === this ? q : p );
}
object = {
"r1n": {
"mkg": {
"zma": [21, 45, 66, 111],
"mii": {
"ltf": [2, 5, 3, 9, 21]
},
"fv": [1, 3, 6, 9]
},
"rmk": {
"amr": [50, 50, 100, 116, 150, 250]
}
},
"fik": {
"er": [592, 92, 32, 13],
"gp": [12, 34, 116, 29]
}
};
console.log(findRootProperty(object,116))

Convert array of objects to an object of arrays

I have the following structure
var nights = [
{ "2016-06-25": 32, "2016-06-26": 151, "2016-06-27": null },
{ "2016-06-24": null, "2016-06-25": null, "2016-06-26": null },
{ "2016-06-26": 11, "2016-06-27": 31, "2016-06-28": 31 },
];
And I want to transform it to:
{
"2016-06-24": [null],
"2016-06-25": [32, null],
"2016-06-26": [151, null, 11],
"2016-06-27": [null, 31],
"2016-06-28": [31]
}
What's the shortest way to solve this? I have no problems with using Underscore, Lodash or jQuery.
My current code is:
var out = {};
for (var i = 0; i < nights.length; i++) {
for (var key in nights[i]) {
if (out[key] === undefined) {
out[key] = [];
}
out[key].push(nights[i][key]);
}
}
It's similar to Convert array of objects to object of arrays using lodash but that has all keys present in each object.
You can do it with the following snippet (no need for lodash etc):
const x = [{ '2016-06-25': 32, '2016-06-26': 151, '2016-06-27': null }, { '2016-06-24': null, '2016-06-25': null, '2016-06-26': null }, { '2016-06-26': 11, '2016-06-27': 31, '2016-06-28': 31 }, ];
let y = {};
x.forEach(obj => {
Object.keys(obj).forEach(key => {
y[key] = (y[key] || []).concat([obj[key]]);
});
});
console.log(y)
Here is my one-liner. Uses map to map keys into array of objects with properties of the keys. Then maps the original array to only that property and sets it as the value of the property. One issue with this way is that it will only use the properties of the first object in the array. So if other objects have properties that aren't in the first object they will be ignored.
const output = Object.assign({}, ...Object.keys(input[0]).map(props => ({[props]: input.map(prop => prop[props])})))
Edit: the output was in the wrong format, fixed
You could iterate over the array and the over the keys of the object and build a new object with the keys as new keys.
var data = [{ '2016-06-25': 32, '2016-06-26': 151, '2016-06-27': null }, { '2016-06-24': null, '2016-06-25': null, '2016-06-26': null }, { '2016-06-26': 11, '2016-06-27': 31, '2016-06-28': 31 }, ],
result = {};
data.forEach(function (o) {
Object.keys(o).forEach(function (k) {
result[k] = result[k] || [];
result[k].push(o[k]);
});
});
console.log(result);
Used Typescript -- obviously you can remove the types while working in JavaScript.
Assumption: The array of objects coming into the function will always have the same kind and number of object keys
mapperArrayOfJsonsToJsonOfArrays(inputArrayOfJsons: any): any {
if (inputArrayOfJsons.length > 0) {
let resultMap = {};
let keys: any[] = Object.keys(inputArrayOfJsons[0]);
keys.forEach((key: any) => {
resultMap[key] = [];
});
inputArrayOfJsons.forEach((element: any) => {
let values: any[] = Object.values(element);
let index = 0;
values.forEach((value: any) => {
resultMap[keys[index]].push(value);
index = index + 1;
});
});
return resultMap;
}
return {};
}

Grouping Objects In Array Based On Property Name

I have an array with a list of objects:
var myArray = [
{"cartItems": {"paramA1": 25, "paramA2": 35}},
{"cartShippingCost": {"paramB1": 4, "paramB2": 152, "paramB3": 536, "paramB4": 56}},
{"cartSpecialRequirements": {"paramC1": 432}},
{"cartPostage": {"paramD1": 56, "paramD2": 6537}},
{"uid": {"paramE1": 545}},
{"tel": 7778798548}
];
How can i loop through the above and group objects that contain 'cart' and ones that don't?
I know i need to create 2 temp obj's e.g.
var cartObj;
var nonCartObj;
And perform a .push in to each of these if the condition is met.
Tried:
for(var i in myArray) {
if (i == 'cart') {
console.log('cart match found');
}
}
Update:
Using Object.keys always hitting else case:
var key = Object.keys(item);
if (key.indexOf("cart") !== -1) {
alert(key + " contains cart");
} else {
alert(key + " doesnt contain cart");
}
You can use Array.prototype.filter to create a new array where each item satisfies some condition:
var cartItems = myArray.filter(hasCartProperty);
var nonCartItems = myArray.filter(function (x) { return !hasCartProperty(x); });
// Checks to see if any of the object's keys contain the text 'cart'
function hasCartProperty(x) {
return Object.keys(x).some(function (x) {
return x.indexOf('cart') > -1;
});
}
Loop through each item of the array, and then inside that loop iterate over the object keys to find what you're looking for
var result = [];
myArray.forEach(function (item) {
for (var key in item) {
if (key.indexOf('cart') !== -1) {
result.push(item);
}
}
});
console.log(result);
The JavaScript Array object has a filter function that can be used for this.
The tricky part is the function that performs the filtering. One way to do that is to iterate the keys in the objects to detect which ones contain "cart" keys.
var myArray = [
{"cartItems": {"paramA1": 25, "paramA2": 35}},
{"cartShippingCost": {"paramB1": 4, "paramB2": 152, "paramB3": 536, "paramB4": 56}},
{"cartSpecialRequirements": {"paramC1": 432}},
{"cartPostage": {"paramD1": 56, "paramD2": 6537}},
{"uid": {"paramE1": 545}},
{"tel": 7778798548}
];
var cartObj = myArray.filter(function (x) { return isCart(x); });
var nonCartObj = myArray.filter(function (x) { return !isCart(x); });
console.log("cart count = " + cartObj.length + ", non-cart count = " + nonCartObj.length);
function isCart(x) {
for (var key in x) {
if (x.hasOwnProperty(key) && key.startsWith("cart")) {
return true;
}
}
return false;
}
A solution with one loop without Array#filter().
var myArray = [{ "cartItems": { "paramA1": 25, "paramA2": 35 } }, { "cartShippingCost": { "paramB1": 4, "paramB2": 152, "paramB3": 536, "paramB4": 56 } }, { "cartSpecialRequirements": { "paramC1": 432 } }, { "cartPostage": { "paramD1": 56, "paramD2": 6537 } }, { "uid": { "paramE1": 545 } }, { "tel": 7778798548 }],
grouped = function (array) {
var r = { cart: [], nonCart: [] };
array.forEach(function (a) {
Object.keys(a).some(function (k) {
if (k.indexOf('cart') === 0) {
return r.cart.push(a);
}
}) || r.nonCart.push(a);
});
return r;
}(myArray);
document.write('<pre>' + JSON.stringify(grouped, 0, 4) + '</pre>');

Group objects based on property name and type

I have an array as per below;
var myArray = {
"cartItems": {"paramA1": 25, "paramA2": 35},
"cartShippingCost": {"paramB1": 4, "paramB2": 152, "paramB3": 536, "paramB4": 56},
"cartNo": 675765,
"cartSpecialRequirements": {"paramC1": 432},
"cartPostage": {"paramD1": 56, "paramD2": 6537},
"cartId": 54,
"tel": 7778798548,
"status": 5465465,
"delivery": 65464646686674
};
var cartItems;
I perform a loop over myArray to find the objects who' key contains 'cart'
for (var key in myArray) {
if (myArray.hasOwnProperty(key)) {
if (key.indexOf('cart') > -1) {
alert(key + " -> " + o[key]);
}
}
}
Which works great. I want to retrieve objects:
with keys containing 'cart'
and is of type structure, e.g.from the above, cartNo and cartId are NOT of type structures.
push the 'cart' structures type in to cartItems;
like this?
var data = {
"cartItems": {"paramA1": 25, "paramA2": 35},
"cartShippingCost": {"paramB1": 4, "paramB2": 152, "paramB3": 536, "paramB4": 56},
"cartNo": 675765,
"cartSpecialRequirements": {"paramC1": 432},
"cartPostage": {"paramD1": 56, "paramD2": 6537},
"cartId": 54,
"tel": 7778798548,
"status": 5465465,
"delivery": 65464646686674
}
var cartObjects = [];
for(var k in data){
var v = data[k];
if(v && typeof v === "object" && k.indexOf("cart") !== -1 && data.hasOwnProperty(k)){
cartObjects.push(v);
}
}
console.log(cartObjects)
You can check if a variable is an object (structure as you said) by using typeof(var). In your case, you could do something like this in your loop :
if (typeof(myArray[key]) == 'object')
{
// do something
}
Hope it helps!
You just need to first filter your object for keys which contain 'cart' and they reference properties of type 'object'. Then map them to the referenced object. This will result in an array of objects.
var cartItems = Object.keys(myArray)
.filter(key => {
return key.indexOf('cart') > -1 && typeof myArray[key] === 'object';
})
.map(key => {
return myArray[key];
});
You may do it like this
var myArray = {
"cartItems": {"paramA1": 25, "paramA2": 35},
"cartShippingCost": {"paramB1": 4, "paramB2": 152, "paramB3": 536, "paramB4": 56},
"cartNo": 675765,
"cartSpecialRequirements": {"paramC1": 432},
"cartPostage": {"paramD1": 56, "paramD2": 6537},
"cartId": 54,
"tel": 7778798548,
"status": 5465465,
"delivery": 65464646686674
};
var result = Object.keys(myArray).reduce(function (p, c) {
var value = myArray[c];
if (c === "cartItems" || c.indexOf("cart") < 0) { return p; }
if (["string", "number", "boolean"].indexOf(typeof value) > -1) {
p.push(value);
}
return p;
}, []);
document.write(JSON.stringify(result));

Categories