JS find all sequences in array - javascript

Giving an array (fixed length) of objects with the following structures:
{type: 'A', value: 1}
or
{type: 'B', text: 'b'}
What is the easiest way to find all the sequences of objects of type 'A' and return their indices?
An example:
For the following array:
[
{type: 'A', value: 1}, {type: 'A', value: 2}, {type: 'B', text: 'b1'},
{type: 'A', value: 11}, {type: 'A', value: 12}, {type: 'A', value: 13},
{type: 'B', text: 'b2'}, {type: 'A', value: 10}, {type: 'B', text: 'b3'}
]
The output should be the following array:
[
{startIndex: 0, startValue: 1, length: 2},
{startIndex: 3, startValue: 11, length: 3},
{startIndex: 7, startValue: 10, length: 1},
]
I guess the naive implementation would be to iterate with forEach and have many complex conditions but is there a simpler technique?
Thanks.

You could use reduce like this. Add a variable prev to keep track of what the previous type was. If the current type is the type you are looking for: Add an object if the previous item had a different type. Else, just increment the length property
let input = [{type:'A',value:1},{type:'A',value:2},{type:'B',text:'b1'},{type:'A',value:11},{type:'A',value:12},{type:'A',value:13},{type:'B',text:'b2'},{type:'A',value:10},{type:'B',text:'b3'}],
prev;
const output = input.reduce((acc, { type, value }, i) => {
if (type === 'A') {
if (prev !== type) {
acc.push({ startIndex: i, startValue: value, length: 1 })
} else {
acc[acc.length - 1].length++
}
}
prev = type
return acc;
}, [])
console.log(output)

You could reduce the array and chweck the value and the type for creating a new group.
var array = [{ type: 'A', value: 1 }, { type: 'A', value: 2 }, { type: 'B', text: 'b1' }, { type: 'A', value: 11 }, { type: 'A', value: 12 }, { type: 'A', value: 13 }, { type: 'B', text: 'b2' }, { type: 'A', value: 10 }, { type: 'B', text: 'b3' }],
result = array.reduce((r, { type, value }, i, a) => {
var previous = a[i - 1] || {},
last = r[r.length - 1];
if (!isFinite(value) || type !== 'A') return r;
if (previous.type !== type) {
r.push({ startIndex: i, startValue: value, length: 1 });
return r;
}
if (value === a[last.startIndex].value + last.length) last.length++;
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Just use a forEach loop. its simple to read and doesnt complicate your code.
let arr = [
{type: 'A', value: 1}, {type: 'A', value: 2}, {type: 'B', text: 'b1'},
{type: 'A', value: 11}, {type: 'A', value: 12}, {type: 'A', value: 13},
{type: 'B', text: 'b2'}, {type: 'A', value: 10}, {type: 'B', text: 'b3'}
]
function findSequences(arr, character) {
let seq = []
let seqStarted = false;
let seqI = 0;
arr.forEach((el, i) => {
if (el.type == character) {
if (seqStarted == true) {
seq[seqI].length += 1;
} else {
seqStarted = true;
seq.push({
startIndex: i,
startValue: el.value,
length: 1
});
}
} else {
if (seqStarted) {
seqStarted = false;
seqI++;
}
}
})
return seq;
}
console.log(findSequences(arr, 'A'))

I don't think it can get any simpler that just iterating over the array using a for loop and constructing your desired answer. Also, this solution has linear complexity, as you're traversing the array just once. Not sure why you'd need "many complex conditions" though.
Something like this seems pretty OK to me:
const arr = [
{type: 'A', value: 1}, {type: 'A', value: 2}, {type: 'B', text: 'b1'},
{type: 'A', value: 11}, {type: 'A', value: 12}, {type: 'A', value: 13},
{type: 'B', text: 'b2'}, {type: 'A', value: 10}, {type: 'B', text: 'b3'}
];
const result = [];
for (let [index, el] of arr.entries()) {
if (el.type === 'A') {
// account for the first entry found
if (result.length === 0) {
result.push({startIndex: index, length: 1, startValue: el.value});
} else {
const lastSequence = result[result.length - 1];
// check if the we are in a sequence
if (lastSequence.startIndex + lastSequence.length === index) {
lastSequence.length += 1;
} else {
// if we are not in a sequence - create a new one
result.push({startIndex: index, length: 1, startValue: el.value});
}
}
}
}
console.log(result);

For your specified array:
var arr =[
{type: 'A', value: 1}, {type: 'A', value: 2}, {type: 'B', text: 'b1'},
{type: 'A', value: 11}, {type: 'A', value: 12}, {type: 'A', value: 13},
{type: 'B', text: 'b2'}, {type: 'A', value: 10}, {type: 'B', text: 'b3'}
];
arr.reduce((result, current, index) => {
if(current.type == 'A'){
if(result.length == 0 || index - result[result.length - 1].length != result[result.length - 1]. startIndex){
result.push({startIndex: index, startValue: current.value, length: 1})
}
else{
result[result.length - 1].length++
}
}
return result;
}, [])

You can use declarative approach something like this instead imperative with forEach: You can use declarative approach something like this instead imperative with forEach:
const a = [
{type: 'A', value: 1}, {type: 'A', value: 2}, {type: 'B', text: 'b1'},
{type: 'A', value: 11}, {type: 'A', value: 12}, {type: 'A', value: 13},
{type: 'B', text: 'b2'}, {type: 'A', value: 10}, {type: 'B', text: 'b3'}
];
var filtered = a.map((v,i) => {
return v.type == 'A' ? {startIndex: i,startValue: v.value} : null
}).filter(x => x).reduce((acc,v) => {
if (acc.filter(x => x.startIndex + x.length == v.startIndex).length > 0){
acc[acc.length-1].length ++;
} else {
v.length = 1;
acc.push(v);
}
return acc;
},[]);
console.log(filtered);

Though there are already lots of good answers here, I thought I'd add one which addresses a more general case of segmenting a list. With this code, it's possible to specify a segmenter function, which compares two items and determines whether a new segment should be started.
Once you have these segments, getting to the final answer you require is quite simple.
const data = [
{type: 'A', value: 1}, {type: 'A', value: 2}, {type: 'B', text: 'b1'},
{type: 'A', value: 11}, {type: 'A', value: 12}, {type: 'A', value: 13},
{type: 'B', text: 'b2'}, {type: 'A', value: 10}, {type: 'B', text: 'b3'}
]
const segmentBy = segmenter => items => {
const segmentReducer = (prev = [], curr) => {
let lastSegment = [];
let lastItem = null;
try {
lastSegment = prev[prev.length - 1];
lastItem = lastSegment[lastSegment.length - 1];
} catch (e) {
return [...prev, [curr]];
}
const requiresNewSegment = segmenter(lastItem, curr);
if (requiresNewSegment) {
return [...prev, [curr]];
}
return [...prev.slice(0, prev.length - 1), [...lastSegment, curr]];
};
return items.reduce(segmentReducer, []);
};
const addIndex = a => a.map((x, i) => ({...x, index: i}))
const segmentByType = segmentBy((a, b) => a.type !== b.type);
const segments = segmentByType(addIndex(data));
const result = segments
.map(segment => ({
startIndex: segment[0].index,
startValue: segment[0].value || null,
length: segment.length
}))
.filter(x => x.startValue !== null)
console.dir(result);

Related

filtering nested objects in javascript

I have the following nested objects in javascript and I would like to filter based on a specific condition (value < 7). How can I do this in JS?
Input:
const data = [
{key: 'a', value: [{key: 'aa', value: 6}, {key: 'ab', value: 10}]},
{key: 'b', value: [{key: 'ba', value: 3}, {key: 'bb', value: 11}]}
]
Expected output:
const data = [
{key: 'a', value: [{key: 'aa', value: 6}]},
{key: 'b', value: [{key: 'ba', value: 3}]}
]
I have tried already some and filter but it is not working as expected.
Thanks!
Assuming you can use arrow functions:
const filteredData = data.map(item => {
item.value = item.value.filter(subItem => subItem.value < 7);
return item;
});
And if you're working in an environment where you can use the spread operator:
const filteredData = data.map(item => ({
...item,
value: item.value.filter(subItem => subItem.value < 7),
}));
Recursive function, without touching the original object
const data = [
{key: 'a', value: [{key: 'aa', value: 6}, {key: 'ab', value: 10}]},
{key: 'b', value: [{key: 'ba', value: 3}, {key: 'bb', value: 11}]}
];
const filterData = (dataOriginal, minValue) => {
//INFO: create copy of the object
const d = JSON.parse(JSON.stringify(dataOriginal));
//-----
return d.filter((item,i) => {
if(typeof item.value === 'number'){
return item.value < minValue;
} else if(Array.isArray(item.value)) {
const f = filterData(item.value, minValue);
d[i].value = f;
return !!f.length;
}
});
};
const filtered = filterData(data, 7);
console.log(filtered);
There are many ways, but how about e.g. this:
for (const o of data) o.value = o.value.filter(e => e.value < 7);

How to get the intersection of two sets while recognizing equal set values/items not only by reference but by their equal structures and entries too?

I have two deal with two Set instances.
const set1 = new Set([
{ name: 'a' },
{ name: 'b', lastname: 'bb' },
{ name: 'c' },
{ name: 'd' },
]);
const set2 = new Set([
{ name: 'b' },
{ name: 'd' },
]);
Any object within a set will feature several and also distinct keys and values. The goal is to find structurally equal objects (same keys and values) in both sets, which is ... The intersection of equal data items in/of set1 and set2.
In the following example the expected result is [ { name: 'd' } ] ...
console.log([...set1].filter(item => set2.has(item)));
... but it logs an empty array / [] instead.
An object features more than 20 keys so one has to compare them one by one, which can not be done in a hard coded way.
How could one achieve a generic approach for an intersection of two lists of structurally equal data items?
You can do something like this:
const set1 = new Set([
{name: 'a'},
{name: 'b', lastname: 'bb'},
{name: 'c'},
{name: 'd'}
]);
const set2 = new Set([
{name: 'b'},
{name: 'd'}
]);
set1.forEach((value) => {
if (![...set2].some((o) => Object.entries(o).every(([k, v], _, arr) => (Object.keys(value).length === arr.length && value[k] === v)))) {
set1.delete(value);
}
})
console.log([...set1]);
What this does, is to iterate through set1 and if the item at the current iteration is not the same as any item in set2 (![...set2].some(..)), it is deleted.
The items are considered the same if they have the same number of keys and if the values at the same key are strictly equal.
This only works if the values of the objects in the sets are primitives, if they are not, you'll have to change value[k] === v to an appropriate comparison.
One could write a generic solution which compares pure, thus JSON conform, data structures regardless of any object's nesting depth/level and (creation time) key order.
Such a function would be self recursive for Array item (order matters) and Object property (key order does not matter) comparison. Otherwise values are compared strictly.
function isDeepDataStructureEquality(a, b) {
let isEqual = Object.is(a, b);
if (!isEqual) {
if (Array.isArray(a) && Array.isArray(b)) {
isEqual = (a.length === b.length) && a.every(
(item, idx) => isDeepDataStructureEquality(item, b[idx])
);
} else if (
a && b
&& (typeof a === 'object')
&& (typeof b === 'object')
) {
const aKeys = Object.keys(a);
const bKeys = Object.keys(b);
isEqual = (aKeys.length === bKeys.length) && aKeys.every(
(key, idx) => isDeepDataStructureEquality(a[key], b[key])
);
}
}
return isEqual;
}
const objA = { // `objA` equals `objB`.
name: 'foo',
value: 1,
obj: {
z: 'z',
y: 'y',
a: {
name: 'bar',
value: 2,
obj: {
x: 'x',
w: 'w',
b: 'b',
},
arr: ['3', 4, 'W', 'X', {
name: 'baz',
value: 3,
obj: {
k: 'k',
i: 'i',
c: 'c',
},
arr: ['5', 6, 'B', 'A'],
}],
},
},
arr: ['Z', 'Y', 1, '2'],
};
const objB = { // `objB` equals `objA`.
arr: ['Z', 'Y', 1, '2'],
obj: {
z: 'z',
y: 'y',
a: {
obj: {
x: 'x',
w: 'w',
b: 'b',
},
arr: ['3', 4, 'W', 'X', {
obj: {
k: 'k',
i: 'i',
c: 'c',
},
name: 'baz',
value: 3,
arr: ['5', 6, 'B', 'A'],
}],
name: 'bar',
value: 2,
},
},
name: 'foo',
value: 1,
};
const objC = { // `objC` equals neither `objA` nor `objB`.
arr: ['Z', 'Y', 1, '2'],
obj: {
z: 'z',
y: 'y',
a: {
obj: {
x: 'x',
w: 'w',
b: 'b',
},
arr: ['3', 4, 'W', 'X', {
obj: {
k: 'k',
i: 'i',
c: 'C', // the single difference to `objA` and `objB`.
},
name: 'baz',
value: 3,
arr: ['5', 6, 'B', 'A'],
}],
name: 'bar',
value: 2,
},
},
name: 'foo',
value: 1,
};
console.log(
'isDeepDataStructureEquality(objA, objB) ?..',
isDeepDataStructureEquality(objA, objB)
);
console.log(
'isDeepDataStructureEquality(objA, objC) ?..',
isDeepDataStructureEquality(objA, objC)
);
console.log(
'isDeepDataStructureEquality(objB, objC) ?..',
isDeepDataStructureEquality(objB, objC)
);
console.log(
'isDeepDataStructureEquality(objB, objA) ?..',
isDeepDataStructureEquality(objB, objA)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
Based on the above implementation of isDeepDataStructureEquality one can solve the OP's task, that actually looks for the intersection of two list structures, by additionally providing a getIntersectionOfDeeplyEqualDataStructures functionality ...
function getIntersectionOfDeeplyEqualDataStructures(a, b) {
return [...(a ?? [])]
.reduce((collector, sourceItem) => {
const { target, intersection } = collector;
const targetIndex = target.findIndex(targetItem =>
isDeepDataStructureEquality(targetItem, sourceItem)
);
if (targetIndex >= 0) {
// collect the intersection of
// both, source (a) and target (b).
intersection.push(target[targetIndex]);
}
return collector;
}, {
target: [...(b ?? [])],
intersection: [],
}).intersection;
}
const set1 = new Set([
{ name: 'a' },
{ name: 'b', lastname: 'bb' },
{ name: 'c' },
{ name: 'd' }
]);
const set2 = new Set([
{ name: 'b' },
{ name: 'd' },
]);
console.log(
"getIntersectionOfDeeplyEqualDataStructures(set1, set2) ...",
getIntersectionOfDeeplyEqualDataStructures(set1, set2)
);
const set3 = new Set([
{ name: 'a' },
{ name: 'b', lastname: 'bb' },
{ name: 'c' },
{
name: 'd',
list: ['foo', 1, null, false, 0, {
foo: { bar: { baz: 'bizz', buzz: '' } }
}],
},
]);
const set4 = new Set([
{
list: ['foo', 1, null, false, 0, {
foo: { bar: { buzz: '', baz: 'bizz' } }
}],
name: 'd',
},
{ name: 'C' },
{ lastname: 'bb', name: 'b' },
{ name: 'aa' }
]);
console.log(
"getIntersectionOfDeeplyEqualDataStructures(set3, set4) ...",
getIntersectionOfDeeplyEqualDataStructures(set3, set4)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
<script>
function isDeepDataStructureEquality(a, b) {
let isEqual = Object.is(a, b);
if (!isEqual) {
if (Array.isArray(a) && Array.isArray(b)) {
isEqual = (a.length === b.length) && a.every(
(item, idx) => isDeepDataStructureEquality(item, b[idx])
);
} else if (
a && b
&& (typeof a === 'object')
&& (typeof b === 'object')
) {
const aKeys = Object.keys(a);
const bKeys = Object.keys(b);
isEqual = (aKeys.length === bKeys.length) && aKeys.every(
(key, idx) => isDeepDataStructureEquality(a[key], b[key])
);
}
}
return isEqual;
}
</script>
Edit
As for Titus' approach ...
set1.forEach(value => {
if (
![...set2].some(o =>
Object.entries(o).every(([k, v], _, arr) =>
(Object.keys(value).length === arr.length && value[k] === v)
)
)
) {
set1.delete(value);
}
});
... which works for flat objects only, though already agnostic to key insertion order, one could optimize the code by ...
... not creating the keys array of the most outer currently processed object again and again with every nested some and every iteration.
thus, something like ... const valueKeys = Object.keys(value); ... before the if clause, already helps improving the code.
... inverting the nested some and every logic which does result in a more efficient way of ... deleting every flat data-item from the processed set which does not equal any flat data-item from the secondary set.
On top of that, one could implement a function statement which not only helps code-reuse but also makes the implementation independent from outer scope references.
For instance, the primary set which is operated and going to be mutated can be accessed as such a function's third parameter. But most important for outer scope independency is the also available thisArg binding for any set's forEach method. Thus any function statement or function expression can access e.g. the other/secondary set via this in case the latter was passed as the forEach's 2nd parameter.
Also an improved wording supports a better readability of the code ...
//the function naming of cause is exaggerated.
function deleteItemFromSourceWhichDoesNotEqualAnyItemFromBoundTarget(sourceItem, _, sourceSet) {
const targetSet = this;
const sourceKeys = Object.keys(sourceItem);
if (
// ... for any data-item from the (bound) target-set ...
[...targetSet].every(targetItem =>
// ... which does not equal the currently processed data-item from the source-set ...
Object.entries(targetItem).some(([targetKey, targetValue], _, targetEntries) =>
sourceKeys.length !== targetEntries.length || sourceItem[targetKey] !== targetValue
)
)
) {
// ... delete the currently processed data-item from the source-set.
sourceSet.delete(sourceItem);
}
}
const set1 = new Set([
{ name: 'a' }, // - to be kept.
{ name: 'b', lastname: 'bb' }, // - to be kept.
{ name: 'c' }, // - to be deleted.
{ name: 'd', nested: { name: 'a' } }, // - to be kept, but fails ...
]); // ... due to not being flat.
const set2 = new Set([
{ name: 'd', nested: { name: 'a' } }, // - should equal, but doesn't.
{ name: 'a' }, // - does equal.
{ lastname: 'bb', name: 'b' }, // - does equal.
{ name: 'e' }, // - doesn't equal.
]);
// `set1` is going to be mutated.
set1.forEach(deleteItemFromSourceWhichDoesNotEqualAnyItemFromBoundTarget, set2);
console.log(
'mutated `set1` now (almost) being equal to the intersection of initial `set1` and `set2` ...',
[...set1]
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
const set1 = new Set([
{name: 'a'},
{name: 'b', lastname: 'bb'},
{name: 'c'},
{name: 'd'}
]);
const set2 = new Set([
{name: 'b'},
{name: 'd'}
]);
const names = [...set2].map(s2 => s2.name);
console.log([...set1].filter(item => names.includes(item.name)));
const set1 = new Set([
{name: 'a'},
{name: 'b', lastname: 'bb'},
{name: 'c'},
{name: 'd'},
{name: 'e'}
]);
const set2 = new Set([
{name: 'c', lastname: 'ccc'},
{name: 'd'},
{name: 'b', lastname: 'cc'},
{name: 'e'}
]);
console.log([...set1].filter(item => {
const s2Arr = [...set2];
const itemKeys = Object.keys(item);
for(let i = 0; i < s2Arr.length; i++){
const s2Obj = s2Arr[i];
const s2ObjKeys = Object.keys(s2Obj);
if(s2ObjKeys.length == itemKeys.length){
let oneSame = true;
for(let j = 0; j < s2ObjKeys.length; j++){
const s2ObjKey = s2ObjKeys[j];
if(item[s2ObjKey] != s2Obj[s2ObjKey]){
oneSame = false;
}
}
if(oneSame)
return true;
}
}
return false;
}));

Use .reduce to sum object variabele based on object enum

I'm trying to use the .reduce method to sum the amount of different Expense objects. These Expenses can be of different type, and I'd like to keep the objects in the array splitted by that. So for example, the following array of expenses:
[
{type: A, amount: 2},
{type: A, amount: 1},
{type: B, amount: 2},
{type: A, amount: 3},
{type: C, amount: 2},
{type: B, amount: 1}
]
Should become this:
[
{type: A, amount: 6},
{type: B, amount: 3},
{type: C, amount: 2}
]
Also note that there should not be amount: 0 if no expenses of that type are present, but just an expense less. So without expenses of type C, the result should look like:
[
{type: A, amount: 6},
{type: B, amount: 3}
]
This is how far I got:
private joinExpenses(expenses: Expense[]): Expense[] {
// Add all expenses amount splitted by payment type and store in object
const expenseArrayAsObject = expenses.reduce(
(expensesObject, item) => {
const type = item.type;
if (!expensesObject.hasOwnProperty(type)) {
expensesObject[type] = {
type: type,
amount: {
amount: 0
} as Money
} as Expense;
}
const expense: Expense = expensesObject[type];
expense.amount.amount = expense.amount.amount + item.amount.amount;
expensesObject[type] = expense;
return expensesObject;
},
{} as { [key: string]: any }
);
// Convert object to array
const joinedExpenses: Expense[] = [];
for (const key in expenseArrayAsObject) {
joinedExpenses.push(expenseArrayAsObject[key]);
}
return joinedExpenses;
}
This works, but I feel like mapping to the object first and converting that to an array is a step too much, and can be simplified. I can do some manipulation afterwards but I feel like I iterate through arrays too much.. I just don't see how. Can you help me?
You can use as the following with .reduce() and .find() combination:
const data = [
{type: 'A', amount: 2},
{type: 'A', amount: 1},
{type: 'B', amount: 2},
{type: 'A', amount: 3},
{type: 'C', amount: 2},
{type: 'B', amount: 1}
];
const result = data.reduce((a, c) => {
const found = a.find(e => e.type === c.type);
if (found) found.amount = found.amount + c.amount;
return found ? a : a.concat(c);
}, []);
console.log(result);
Is this what you need ?
const array = [
{type: 'A', amount: 2},
{type: 'A', amount: 1},
{type: 'B', amount: 2},
{type: 'A', amount: 3},
{type: 'C', amount: 2},
{type: 'B', amount: 1}
];
const result = array.reduce((acc, item) => {
const current = acc.find(el => el.type === item.type);
if(!current) {
acc.push(item)
}else{
current.amount += item.amount;
}
return acc;
}, []);
console.log(result);
Inside reduce callback use findIndex to check if an object exist in accumulator accray with same key. Then update the value of amount, else create the object
let data = [{
type: 'A',
amount: 2
},
{
type: 'A',
amount: 1
},
{
type: 'B',
amount: 2
},
{
type: 'A',
amount: 3
},
{
type: 'C',
amount: 2
},
{
type: 'B',
amount: 1
}
]
let newData = data.reduce((acc, curr) => {
let ifKeyExist = acc.findIndex((item) => {
return item.type === curr.type;
});
if (ifKeyExist === -1) {
acc.push({
type: curr.type,
amount: curr.amount
});
} else {
acc[ifKeyExist].amount += curr.amount;
}
return acc;
}, []);
console.log(newData)
reduce over the array using an object as an accumulator. Use the type as the key, and assign the new object to it. Then use Object.values to grab the output you need.
const data = [
{type: 'A', amount: 2},
{type: 'A', amount: 1},
{type: 'B', amount: 2},
{type: 'A', amount: 3},
{type: 'C', amount: 2},
{type: 'B', amount: 1}
];
const out = Object.values(data.reduce((acc, { type, amount }) => {
// If type doesn't exist as a key on the accumulator
// add it and set its value to a new object
acc[type] = acc[type] || { type, amount: 0 };
// Increment the object amount value
acc[type].amount += amount;
// Return the accumulator
return acc;
}, {}));
console.log(out);

Filter function that supports more fields at the same time

I have this function that filters through elements. For example if I have this array:
arr = [{'name': 'a', 'group': 'aa'}, {'name': 'b', 'group': 'bb'}, {'name': 'c', 'group': 'cc'}, {'name': 'a', 'group': 'dd'}]
And I filter it by 'name=a', it will return me
{'name': 'a', 'group': 'aa'}
{'name': 'a', 'group': 'dd'}
But how can I put another condition, not just on name. For example I want to search after name=a and group=aa and return:
{'name': 'a', 'group': 'aa'}
This is my function but it only receives one field at the moment
transform(items: any[], field: string, value: string[]): any[] {
if (!items) {
return [];
}
if (!field || !value || value.length <= 0) {
return items;
}
this.newArray = items.filter(singleItem => {
return (singleItem != null && singleItem[field] != null && singleItem[field] != undefined && value.indexOf(singleItem[field]) >= 0);
});
return this.newArray
}
How can I modify it (doing something like value which is a string[])? Thank you for your time!
You could take an object for the wanted filters
{
name: 'a',
group: 'aa'
}
and iterate this key/value by checking it with the object's values.
This approach accepts an arbitrary count of properties.
var array = [{ name: 'a', group: 'aa' }, { name: 'b', group: 'bb' }, { name: 'c', group: 'cc' }, { name: 'a', group: 'dd' }],
filter = { name: 'a', group: 'aa' },
result = array.filter(o => Object.entries(filter).every(([k, v]) => o[k] === v));
console.log(result);
Try this:
this.newArray = items.filter(
singleItem => singleItem && singleItem.name === 'a' && singleItem.group === 'aa'
);

Sort an array of objects according to rank of different object

I have an array of Objects as below.
var options = [
{value: 'b', label: "test1"},
{value: 'a', label: "test12"},
{value: 'c', label: "test123"}
]
and an array of ranks as below.
var ranks = [
{rank: 1, value: "a"},
{rank: 2, value: "b"},
{rank: 3, value: "c"}
]
Now I need to sort the options array according to the rank property in ranks array
Try this:
var options = [
{value: 'b', label: "test1"},
{value: 'a', label: "test12"},
{value: 'c', label: "test123"}
]
var ranks = [
{rank: 1, value: "a"},
{rank: 2, value: "b"},
{rank: 3, value: "c"}
]
options.sort((a, b) => {
return ranks.find(_ => _.value === a.value).rank - ranks.find(_ => _.value === b.value).rank
})
console.log (options)
With ES6, you could use a Map and take this as closure for sorting.
var options = [{ value: 'b', label: "test1" }, { value: 'a', label: "test12" }, { value: 'c', label: "test123" }],
ranks = [{ rank: 1, value: "a" }, { rank: 2, value: "b" }, { rank: 3, value: "c" }];
options.sort(
(m => ({ value: a }, { value: b }) => m.get(a) - m.get(b))
(ranks.reduce((m, { rank, value }) => m.set(value, rank), new Map))
);
console.log(options);
To bring down the complexity and improve performance, you can first create a temp object/map with key as obj.value and rank as value. Then simply use Array.sort to sort on the basis of rank stored in the temp object.
let options = [{value: 'b', label: "test1"},{value: 'a', label: "test12"},{value: 'c', label: "test123"}];
let ranks = [{rank: 1, value: "a"},{rank: 2, value: "b"},{rank: 3, value: "c"}];
let temp = ranks.reduce((a,c) => Object.assign(a,{[c.value]:c.rank}), {});
options.sort((a,b) => temp[a.value] - temp[b.value]);
console.log(options);

Categories