我如何`.filter()`一个数组/对象并返回一个带有原始键的新数组,而不是作为一个索引数组/对象作为过滤器返回?

时间:2019-04-10 08:17:51

标签: javascript ecmascript-6 functional-programming

var obj = {

    111: {
        user_id: 111,
        user_name: "user111",
        isActive: 0
    }, 
    112: {
        user_id: 112,
        user_name: "use112",
        isActive: 1
    }, 
    113: {
        user_id: 113,
        user_name: "use113",
        isActive: 0
    }, 
    ... 
}

我想过滤所有("isActive" === 0),但在返回newObj时,将键设置为相同(等于用户ID):

newObj = {

    111: {
        user_id: 111,
        user_name: "user111",
        isActive: 0
    }, 
    113: {
        user_id: 113,
        user_name: "use113",
        isActive: 0
    }, 
    ... 
}

这就是我现在拥有的:

let newObj = Object.values(obj).filter( user => ( (obj.isActive === 0)));

返回索引键

  • 没有for循环(除非必须使用ES6 .forEach())。
  • 在这种情况下,我希望使用filter / map / reduce的ES6方法来解决此问题。
  • loadash方法还可以,但我仍然想在前面看到一个“香草ES6”示例
  • 如果我能获得使用这些方法以各种方式最好地学习和练习如何过滤数组的技巧(在这里loadash也很酷)。

5 个答案:

答案 0 :(得分:6)

真正的FP方式将是reduce,且对象会重复散布:

const filtered = Object.values(obj).reduce((p, e) => (!e.isActive ? {...p, [e.user_id]: e} : p), {});

const obj = {
    111: {
        user_id: 111,
        user_name: "user111",
        isActive: 0
    }, 
    112: {
        user_id: 112,
        user_name: "use112",
        isActive: 1
    }, 
    113: {
        user_id: 113,
        user_name: "use113",
        isActive: 0
    }
};
const filtered = Object.values(obj).reduce((p, e) => (!e.isActive ? {...p, [e.user_id]: e} : p), {});
console.log(filtered);
.as-console-wrapper {
  max-height: 100% !important;
}

这会创建很多不必要的临时对象,但是通过不修改对象来遵守FP原则(我认为我对FP并不“深入”:)。

稍微修改一下规则,我们可能会修改单个对象而不是创建大量临时对象:

const filtered = Object.values(obj).reduce((newObj, e) => {
  if (!e.isActive) {
    newObj[e.user_id] = e;
  }
  return newObj;
}, {});

const obj = {
    111: {
        user_id: 111,
        user_name: "user111",
        isActive: 0
    }, 
    112: {
        user_id: 112,
        user_name: "use112",
        isActive: 1
    }, 
    113: {
        user_id: 113,
        user_name: "use113",
        isActive: 0
    }
};
const filtered = Object.values(obj).reduce((newObj, e) => {
  if (!e.isActive) {
    newObj[e.user_id] = e;
  }
  return newObj;
}, {});
console.log(filtered);
.as-console-wrapper {
  max-height: 100% !important;
}

(通过滥用逗号运算符,可以用较少的字符来写,但它的维护性较差,更难阅读。)

没有FP限制,我只使用循环:

const filtered = {};
for (const e of Object.values(obj)) {
  if (!e.isActive) {
    filtered[e.user_id] = e;
  }
}

const obj = {
    111: {
        user_id: 111,
        user_name: "user111",
        isActive: 0
    }, 
    112: {
        user_id: 112,
        user_name: "use112",
        isActive: 1
    }, 
    113: {
        user_id: 113,
        user_name: "use113",
        isActive: 0
    }
};
const filtered = {};
for (const e of Object.values(obj)) {
  if (!e.isActive) {
    filtered[e.user_id] = e;
  }
}
console.log(filtered);
.as-console-wrapper {
  max-height: 100% !important;
}

答案 1 :(得分:2)

建议的进行对象转换的“正式”方法是使用Object.entries“线性化”对象,对键/值对执行映射/过滤,然后将它们与Object.fromEntries放回原处。后者是新的,因此您需要一个polyfill。

示例:

// polyfill

Object.fromEntries = Object.fromEntries || function(pairs) {
    let obj = {};
    for (let [k, v] of pairs)
        obj[k] = v;
    return obj;
};


var myObj = {

    111: {
        user_id: 111,
        user_name: "user111",
        isActive: 0
    },
    112: {
        user_id: 112,
        user_name: "use112",
        isActive: 1
    },
    113: {
        user_id: 113,
        user_name: "use113",
        isActive: 0
    },
};

result = Object.fromEntries(
    Object.entries(myObj)
        .filter(([k, v]) => v.isActive));

console.log(result)

自从您请求FP解决方案以来,这里是一种可能的概括:

let apply = (x, fn) => fn(x);

let pipe = (...fns) => x => fns.reduce(apply, x);

let transform = fn => pipe(Object.entries, fn, Object.fromEntries);

let filter = fn => a => a.filter(fn);

let filterObject = fn => transform(filter(fn));

let removeInactive = filterObject(([k, v]) => v.isActive);

console.log(removeInactive(myObj))

FP的目的是根据功能组成表达程序,而不是用reduce编写“由内而外”的循环。

答案 2 :(得分:1)

您可以获取条目,过滤并构建新对象。

var object = { 111: { user_id: 111, user_name: "user111", isActive: 0 }, 112: { user_id: 112, user_name: "use112", isActive: 1 }, 113: { user_id: 113, user_name: "use113", isActive: 0 } },
    result = Object.assign(...Object
        .entries(object)
        .filter(({ 1: { isActive } }) => isActive === 0)
        .map(([k, v]) => ({ [k]: v }))
    );

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

答案 3 :(得分:1)

我将使用带有可迭代功能的生成器函数:

// make object type iterable

function* objEntries(o) {
  for (let k in o)
    yield [k, o[k]];
}

// generator function that takes an iterable

const itFilter = p => function* (ix) {
  for (const x of ix)
    if (p(x))
      yield x;
};

const obj = {
    111: {
        user_id: 111,
        user_name: "user111",
        isActive: 0
    }, 
    112: {
        user_id: 112,
        user_name: "use112",
        isActive: 1
    }, 
    113: {
        user_id: 113,
        user_name: "use113",
        isActive: 0
    }
};

// exhaust the iterator with a strict evaluated fold

const itFoldStrict = f => acc => ix => {
  let acc_ = acc;

  for (const x of ix)
    acc_ = f(acc_) (x);

  return acc_;
};

const ix = itFilter(([k, o]) => o.isActive === 0)
  (objEntries(obj));

// nothin has happened here due to lazy evaluation

// unleash the effect (of constructing the filtered object)

console.log(
  itFoldStrict(acc => ([k, v]) => (acc[k] = v, acc))
    ({}) (ix));

这种算法

  • 很懒
  • 不产生中间值

答案 4 :(得分:0)

以下内容也可以正常工作:

var newObj = Object.entries(obj).filter(value => {return value[1].isActive ===0});