Iterating Through An Object In JavaScript - javascript

If I have an object, like so:
const test = [
{ x: 'A', y:'1' },
{ x: 'A', y:'2' },
{ x: 'B', y:'1' },
{ x: 'A', y:'3' },
{ x: 'C', y:'1' },
];
How can I go through it, and find the in order sequence [A, B, C] from x, where [A, B, C] belongs to a unique y?
So far I tried iterating through the object using a for loop, finding all 'A', 'B', 'C', in order, but I cannot ensure that they all belong to the same y item.

Transform the array into an object of arrays corresponding to only one particular y first:
const test = [
{ x: 'A', y:'1' },
{ x: 'A', y:'2' },
{ x: 'B', y:'1' },
{ x: 'A', y:'3' },
{ x: 'C', y:'1' },
];
const reducedToYs = test.reduce((accum, { x, y }) => {
accum[y] = (accum[y] || '') + x;
return accum;
}, {});
const found = Object.entries(reducedToYs)
.find(([y, str]) => str.includes('ABC'));
console.log('y: ' + found[0] + ', entire string: ' + found[1]);

Create a function that takes the target Y value. Loop through the test array and compare the y value to your target value and if it matches - push the x value into an array. Then return that array.
If a string is required ("ABC)" then rather than creating an array create a string and append the x value to it to build the string that is then returned.
const test = [
{ x: 'A', y:'1' },
{ x: 'A', y:'2' },
{ x: 'B', y:'1' },
{ x: 'A', y:'3' },
{ x: 'C', y:'1' },
];
function testValue(str) {
let xArray = [];
test.forEach(function(item) {
if(item['y'] === str) {xArray.push(item['x'])}
})
return xArray;
}
console.log(testValue('1')) // gives ["A", "B", "C"]

Related

Function doesn't work when tested with typescript?

I have a strange problem. I have the below function in js:
const removeDuplicates = (inputData) => {
return [
...new Map(inputData.map((data) => [JSON.stringify([data.x, data.y, data.v]), data])).values()
];
};
And I'm testing it like so in a jest typescript test:
it('removeDuplicates', () => {
const expectedObject = [
{ x: 'G', y: 'B', v: '12345' },
{ x: 'A', y: 'L', v: '12345' },
{ x: 'A', y: 'W', v: '123' },
{ x: 'A', y: 'W', v: '1234' }
];
const inputData = [
{ x: 'G', y: 'B', v: '12345' },
{ x: 'A', y: 'L', v: '12345' },
{ x: 'A', y: 'L', v: '12345' },
{ x: 'A', y: 'W', v: '123' },
{ x: 'A', y: 'W', v: '1234' },
{ x: 'A', y: 'W', v: '1234' }
];
expect(removeDuplicates(inputData)).toEqual(expectedObject);
});
When the test is in a js file it passes, when I convert the test file to a ts file it, fails and the function returns nothing! Any ideas?
Seems like the compiler doesn't like it without the compiler option ''--downlevelIteration'. Did you set this?
Type 'IterableIterator<unknown>' is not an array type or a string type. Use compiler option '--downlevelIteration' to allow iterating of iterators.ts(2569)
You could use Array.from instead:
type MyThing = { x: string; y: string; v: string };
const removeDuplicates = (inputData: MyThing[]): MyThing[] =>
Array.from(
new Map(
inputData.map((data) => [JSON.stringify([data.x, data.y, data.v]), data])
).values()
);
You should also consider using an external library for such trivial tasks. E.g. Ramda's uniq function should do exactly the same.

How to intersect and combine multiple object arrays using javascript

I'm trying to find the intersection between n >=2 object arrays which share the same key while also combining the object arrays. I'm looking for the most efficient way to do this. The max length of any of the arrays would be around ~2500 objects long.
An example of what I'm looking for :
object1 = [
{
key: 'key1',
x: 'x',
y: 'y',
z: 'z'
},
{
key: 'key2',
x: 'x',
y: 'y',
z: 'z'
}
]
object2 = [
{
key: 'key1',
a: 'a',
b: 'b'
},
{
key: 'key3',
a: 'a',
b: 'b'
}
]
object3 = [
{
key: 'key1',
c: 'c'
},
{
key: 'key4',
c: 'c'
}
]
with the desired output :
object = [
{
key: 'key1',
x: 'x',
y: 'y',
z: 'z',
a: 'a',
b: 'b',
c: 'c'
}
]
To combine two object arrays, I've used the following function :
let map = new Map(object1.map(o => [o['key'], o]));
return object2.reduce((acc, o) => {
let match = map.get(o['key']);
return match ? acc.concat({ ...o, ...match }) : acc;
}, []);
But I'm not sure how to apply this to more arrays in a way that's not resource and time heavy. Any tips would be appreciated. I'm also open for using helper functions like underscore or lodash.
The advantage (i hope) with this code is that it will preprocess all objects first to find intersecting keys before attempting to reduce and combine each one. In the first iteration fkeys flattens all objects together so we can do just one loop.
Separating those 2 processes should reduce overhead. My guess though is that there's a more elegant way to do this. I added in another intersecting key (key2) for demonstration in snippet
let objs = [object1, object2, object3],
fkeys = Object.keys(Object.fromEntries(Object.entries([...objs.flat()].map(o => o.key).reduce((b, a) => {
if (b.hasOwnProperty(a)) b[a] = b[a] + 1;
else b[a] = 1;
return b
}, {})).filter(k => k[1] === objs.length))),
intersections = Object.values([...objs.flat()].filter(o => fkeys.includes(o.key)).reduce((b, a) => {
if (b.hasOwnProperty(a.key)) b[a.key] = { ...b[a.key], ...a};
else b[a.key] = { ...a};
return b}, {}))
object1 = [{
key: 'key1',
x: 'x',
y: 'y',
z: 'z'
},
{
key: 'key2',
g: 'g',
h: 'h',
j: 'j'
}
]
object2 = [{
key: 'key1',
a: 'a',
b: 'b'
},
{
key: 'key2',
y: 'y',
z: 'z'
},
{
key: 'key3',
a: 'a',
b: 'b'
}
]
object3 = [{
key: 'key1',
c: 'c'
},
{
key: 'key2',
r: 'r'
},
{
key: 'key4',
c: 'c'
}
]
let objs = [object1, object2, object3],
fkeys = Object.keys(Object.fromEntries(Object.entries([...objs.flat()].map(o => o.key).reduce((b, a) => {
if (b.hasOwnProperty(a)) b[a] = b[a] + 1;
else b[a] = 1;
return b
}, {})).filter(k => k[1] === objs.length))),
intersections = Object.values([...objs.flat()].filter(o => fkeys.includes(o.key)).reduce((b, a) => {
if (b.hasOwnProperty(a.key)) b[a.key] = { ...b[a.key],
...a
};
else b[a.key] = { ...a
};
return b;
}, {}))
console.log(fkeys)
console.log(intersections)

Remove duplicated combination in array based on index

I have the following data array:
const data = [
{
value: [
'a',
'b',
'a',
'a'
]
},
{
value: [
'c',
'c',
'd',
'c'
]
}
];
So there's is 4 combination here based on index:
combination 1 : a - c (index 0 in each value arrays)
combination 2 : b - c (index 1 in each value arrays)
combination 3 : a - d (index 2 in each value arrays)
combination 4 : a - c (index 3 in each value arrays)
As you can see the first and the last combinations are the same, so i want to remove the second occurrence from each array, the result should be:
[
{
value: [
'a',
'b',
'a'
]
},
{
value: [
'c',
'c',
'd'
]
}
]
You can zip the values arrays from both objects to form an array which looks like:
["a-c", "b-c", ...]
As these are now strings, you can turn this array into a Set using new Set(), which will remove all duplicate occurrences. You can then turn this set back into an array which you can then use .reduce() on to build you array of objects from. For each value you can obtain the list of values by using .split() on the '-', and from that, populate your reduced array.
See example below:
const data = [{ value: [ 'a', 'b', 'a', 'a' ] }, { value: [ 'c', 'c', 'd', 'c' ] } ];
const unq = [...new Set(
data[0].value.map((_,c)=> data.map(({value})=>value[c]).join('-'))
)];
const res = unq.reduce((acc, str) => {
const values = str.split('-');
values.forEach((value, i) => acc[i].value.push(value));
return acc;
}, Array.from({length: data.length}, _ => ({value: []})))
console.log(res);
Limitations of the above method assume that you won't have a - character as your string value. If this is an issue, you can consider using a different delimiter, or find unique values within your array using .filter() instead of a Set.
You could save a lookup object for unique pairs of value based with index
Given your input is, below solution could help you
const data = [
{
value: ["a", "b", "a", "a"],
},
{
value: ["c", "c", "d", "c"],
},
]
const lookup = {}
data[0].value.forEach((_, index) => {
lookup[`${data[0].value[index]}-${data[1].value[index]}`] = true
})
const res = Object.keys(lookup).reduce(
(acc, key) => {
const [val1, val2] = key.split("-")
acc[0].value.push(val1)
acc[1].value.push(val2)
return acc
},
[{ value: [] }, { value: [] }]
)
console.log(res)
Below is a two step solution with a generator function and a single pass.
const data = [ { value: [ 'a', 'b', 'a', 'a' ] }, { value: [ 'c', 'c', 'd', 'c', ] } ];
const zipDataValues = function* (data) {
const iterators = data.map(item => item.value[Symbol.iterator]())
let iterations = iterators.map(iter => iter.next())
while (iterations.some(iteration => !iteration.done)) {
yield iterations.map(iteration => iteration.value)
iterations = iterators.map(iter => iter.next())
}
}
const filterOutDuplicateCombos = function (values) {
const combosSet = new Set(),
resultData = [{ value: [] }, { value: [] }]
for (const [valueA, valueB] of values) {
const setKey = [valueA, valueB].join('')
if (combosSet.has(setKey)) {
continue
}
combosSet.add(setKey)
resultData[0].value.push(valueA)
resultData[1].value.push(valueB)
}
return resultData
}
console.log(
filterOutDuplicateCombos(zipDataValues(data))
) // [ { value: [ 'a', 'b', 'a' ] }, { value: [ 'c', 'c', 'd' ] } ]
Here is a reference on generators and iterators
Filter combinations + sorting by the first occurrence:
const data = [{
value: ['a', 'b', 'a', 'a']
},{
value: ['c', 'c', 'd', 'c']
}];
var res = {}, i, t;
for (i = 0; i < data[0].value.length; ++i) {
res[data[0].value[i]] = res[data[0].value[i]] || {};
res[data[0].value[i]][data[1].value[i]] = true;
}
data[0].value = [];
data[1].value = [];
for (i in res) {
for (t in res[i]) {
data[0].value[data[0].value.length] = i;
data[1].value[data[1].value.length] = t;
}
}
console.log(data);

How to manipulate an array by grouping items into arrays

Let's say I have this array:
let obj = [
{ prop: 'a', val: 2 },
{ prop: 'a', val: 1 },
{ prop: 'b', val: 3 },
{ prop: 'b', val: 1 }
]
Let's say i want to get an array for each existing prop val:
let myArr = [
[ { prop: 'a', val: 2 }, { prop: 'a', val: 1 } ]
[ { prop: 'b', val: 3 }, { prop: 'b', val: 1 } ]
]
how would you do that by using high order functions?
function sumProps(arr) {
const sum = arr.reduce((ac,cv) => {
if (ac[cv] === undefined) ac[cv.prop] = ac[cv.prop] = cv.val;
else ac[cv.prop] += cv.val;
}, {});
const res = [];
for (const p in sum) {
res.push({ prop:p, sum:sum[p] });
}
return res;
}
You could reduce the objects by looking for an object with same prop and update the value.
var array = [{ prop: 'a', val: 2 }, { prop: 'a', val: 1 }, { prop: 'b', val: 3 }, { prop: 'b', val: 1 }],
result = array.reduce((r, o) => {
var temp = r.find(({ prop }) => prop === o.prop);
if (temp) {
temp.val += o.val;
} else {
r.push(Object.assign({}, o));
}
return r
}, []);
console.log(result);

Sorting array by common fields (javasciript)

I have an array of objects and I want to sort it by common types.
Some objects have types with 'x', some with 'y' and some with 'z'.
For now, I'm able to sort it and put all the 'x' in front. However, I would want to do the same for 'y', and 'z' too.
In the end, all 'x' would be in front, then all 'y' and then 'z'.
list.sort((a, b) => {
if (a.type !== b.type) {
if (a.type === 'x') { return -1; }
return 1;
}
return a.name.localeCompare(b.name);
});
Any help will be greatly appreciated.
You could use an object for type with the sort order.
var list = [{ type: 'a', name: 'z' }, { type: 'b', name: 'a' }, { type: 'c', name: 'b' }, { type: 'x', name: 'c' }, { type: 'x', name: 'd' }, { type: 'y', name: 'e' }, { type: 'y', name: 'f' }, { type: 'z', name: 'g' }, { type: 'z', name: 'h' }, ]
list.sort(function (a, b) {
var order = { x: -1, y: -1, z: -1, default: 0 };
return (order[a.type] || order.default) - (order[b.type] || order.default) || a.name.localeCompare(b.name);
});
console.log(list);
.as-console-wrapper { max-height: 100% !important; top: 0; }
It works with
{
f: -2, // top
x: -1, // \
y: -1, // a group after top
z: -1, // /
default: 0 // a default value for not mentioned types for sorting in the middle
a: 1 // after the common parts
d: 2 // bottom
}
One simple solution would be to define a numeric order for the types and use the classic approach to sort an array of objects by a numeric property:
var order = {'x': 0, 'y': 1, 'z': 2}
var data = [
{type: 'z'},
{type: 'y'},
{type: 'x'},
{type: 'x'},
{type: 'y'},
{type: 'z'}
]
var sortedData = data.sort(function(a, b) {
return order[a.type] - order[b.type]
})
console.log(sortedData)
Note that you may want to include some error handling, e.g. for types which are not maintained in the order map.

Categories